ViewModel Locator
-
As part of my own framework I am developing Service and View Model Locators. Both are done, but I have one problem. Here's what I have so far: Interface
public interface IViewModelLocator
{
object Get();void Register(Type viewType, object viewModel);
}
View Model Locator Class
public class ViewModelLocator : IViewModelLocator
{
// Internal list of View Models
private IDictionary _storage;public ViewModelLocator() { \_storage = new Dictionary<Type, object>(); } public object Get<T>() { if (\_storage.ContainsKey(typeof(T))) { return \_storage.FirstOrDefault(x => x.Key == typeof(T)).Value; } else { throw new Exception($"The requested view '{typeof(T)}' is not registered"); } } public void Register(Type viewType, object viewModel) { if (!\_storage.ContainsKey(viewType)) { \_storage.Add(viewType, viewModel); } else { throw new Exception($"An instance of the view model '{viewModel}' is already registered for the view '{viewType}'"); } }
}
App Setup
public partial class App : Application
{
public static IServiceLocator ServiceLocator { get; private set; }
public static IViewModelLocator ViewModelLocator { get; private set; }public App() { ServiceLocator = new ServiceLocator(); ServiceLocator.Register(typeof(IMyService), new MyService()); ViewModelLocator = new ViewModelLocator(); ViewModelLocator.Register(typeof(MainWindowView), new MainWindowViewModel(ServiceLocator.Get<IMyService>())); ViewModelLocator.Register(typeof(LoginView), new LoginViewModel()); }
}
Window Code Behind
public partial class MainWindowView : Window
{
public MainWindowView()
{
InitializeComponent();this.DataContext = App.ViewModelLocator.Get<MainWindowView>(); }
}
Problem Everything to this point works fine, except that I don't want to have to call the VM locator in code in the code behind. I would like to use it in XAML. I have seen other approaches where they do this:
x:Class="My.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
DataContext="{Binding Source={StaticResource MyViewModelLocator}, Path=Main}"&g -
As part of my own framework I am developing Service and View Model Locators. Both are done, but I have one problem. Here's what I have so far: Interface
public interface IViewModelLocator
{
object Get();void Register(Type viewType, object viewModel);
}
View Model Locator Class
public class ViewModelLocator : IViewModelLocator
{
// Internal list of View Models
private IDictionary _storage;public ViewModelLocator() { \_storage = new Dictionary<Type, object>(); } public object Get<T>() { if (\_storage.ContainsKey(typeof(T))) { return \_storage.FirstOrDefault(x => x.Key == typeof(T)).Value; } else { throw new Exception($"The requested view '{typeof(T)}' is not registered"); } } public void Register(Type viewType, object viewModel) { if (!\_storage.ContainsKey(viewType)) { \_storage.Add(viewType, viewModel); } else { throw new Exception($"An instance of the view model '{viewModel}' is already registered for the view '{viewType}'"); } }
}
App Setup
public partial class App : Application
{
public static IServiceLocator ServiceLocator { get; private set; }
public static IViewModelLocator ViewModelLocator { get; private set; }public App() { ServiceLocator = new ServiceLocator(); ServiceLocator.Register(typeof(IMyService), new MyService()); ViewModelLocator = new ViewModelLocator(); ViewModelLocator.Register(typeof(MainWindowView), new MainWindowViewModel(ServiceLocator.Get<IMyService>())); ViewModelLocator.Register(typeof(LoginView), new LoginViewModel()); }
}
Window Code Behind
public partial class MainWindowView : Window
{
public MainWindowView()
{
InitializeComponent();this.DataContext = App.ViewModelLocator.Get<MainWindowView>(); }
}
Problem Everything to this point works fine, except that I don't want to have to call the VM locator in code in the code behind. I would like to use it in XAML. I have seen other approaches where they do this:
x:Class="My.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
DataContext="{Binding Source={StaticResource MyViewModelLocator}, Path=Main}"&gYou can't reference generic class in Xaml... However I think you can do something! :) How about something like that (pseudo code that might be inspiring)?
DataContext="{Binding Converter={x:Static TestApp:App.ViewModelConverter}, ConvertParameter={x:Type local:MyVMType}}"
Use a Converter with a ConverterParameter? ;)
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
As part of my own framework I am developing Service and View Model Locators. Both are done, but I have one problem. Here's what I have so far: Interface
public interface IViewModelLocator
{
object Get();void Register(Type viewType, object viewModel);
}
View Model Locator Class
public class ViewModelLocator : IViewModelLocator
{
// Internal list of View Models
private IDictionary _storage;public ViewModelLocator() { \_storage = new Dictionary<Type, object>(); } public object Get<T>() { if (\_storage.ContainsKey(typeof(T))) { return \_storage.FirstOrDefault(x => x.Key == typeof(T)).Value; } else { throw new Exception($"The requested view '{typeof(T)}' is not registered"); } } public void Register(Type viewType, object viewModel) { if (!\_storage.ContainsKey(viewType)) { \_storage.Add(viewType, viewModel); } else { throw new Exception($"An instance of the view model '{viewModel}' is already registered for the view '{viewType}'"); } }
}
App Setup
public partial class App : Application
{
public static IServiceLocator ServiceLocator { get; private set; }
public static IViewModelLocator ViewModelLocator { get; private set; }public App() { ServiceLocator = new ServiceLocator(); ServiceLocator.Register(typeof(IMyService), new MyService()); ViewModelLocator = new ViewModelLocator(); ViewModelLocator.Register(typeof(MainWindowView), new MainWindowViewModel(ServiceLocator.Get<IMyService>())); ViewModelLocator.Register(typeof(LoginView), new LoginViewModel()); }
}
Window Code Behind
public partial class MainWindowView : Window
{
public MainWindowView()
{
InitializeComponent();this.DataContext = App.ViewModelLocator.Get<MainWindowView>(); }
}
Problem Everything to this point works fine, except that I don't want to have to call the VM locator in code in the code behind. I would like to use it in XAML. I have seen other approaches where they do this:
x:Class="My.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
DataContext="{Binding Source={StaticResource MyViewModelLocator}, Path=Main}"&gKevin Marois wrote:
public object Get<T>()
{
if (_storage.ContainsKey(typeof(T)))
{
return _storage.FirstOrDefault(x => x.Key == typeof(T)).Value;
}
else
{
throw new Exception($"The requested view '{typeof(T)}' is not registered");
}
}That's a very strange way of using a dictionary. The alternative is simpler and significantly more efficient:
public object Get<T>()
{
if (!_storage.TryGetValue(typeof(T), out var result)) return result;
throw new ArgumentException($"The requested view '{typeof(T)}' is not registered");
}And the method doesn't have to be generic - you can have a non-generic overload as well:
public object Get(Type viewType)
{
if (viewType is null) throw new ArgumentNullException(nameof(viewType));
if (!_storage.TryGetValue(viewType, out var result)) return result;
throw new ArgumentException($"The requested view '{viewType}' is not registered", nameof(viewType));
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
As part of my own framework I am developing Service and View Model Locators. Both are done, but I have one problem. Here's what I have so far: Interface
public interface IViewModelLocator
{
object Get();void Register(Type viewType, object viewModel);
}
View Model Locator Class
public class ViewModelLocator : IViewModelLocator
{
// Internal list of View Models
private IDictionary _storage;public ViewModelLocator() { \_storage = new Dictionary<Type, object>(); } public object Get<T>() { if (\_storage.ContainsKey(typeof(T))) { return \_storage.FirstOrDefault(x => x.Key == typeof(T)).Value; } else { throw new Exception($"The requested view '{typeof(T)}' is not registered"); } } public void Register(Type viewType, object viewModel) { if (!\_storage.ContainsKey(viewType)) { \_storage.Add(viewType, viewModel); } else { throw new Exception($"An instance of the view model '{viewModel}' is already registered for the view '{viewType}'"); } }
}
App Setup
public partial class App : Application
{
public static IServiceLocator ServiceLocator { get; private set; }
public static IViewModelLocator ViewModelLocator { get; private set; }public App() { ServiceLocator = new ServiceLocator(); ServiceLocator.Register(typeof(IMyService), new MyService()); ViewModelLocator = new ViewModelLocator(); ViewModelLocator.Register(typeof(MainWindowView), new MainWindowViewModel(ServiceLocator.Get<IMyService>())); ViewModelLocator.Register(typeof(LoginView), new LoginViewModel()); }
}
Window Code Behind
public partial class MainWindowView : Window
{
public MainWindowView()
{
InitializeComponent();this.DataContext = App.ViewModelLocator.Get<MainWindowView>(); }
}
Problem Everything to this point works fine, except that I don't want to have to call the VM locator in code in the code behind. I would like to use it in XAML. I have seen other approaches where they do this:
x:Class="My.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
DataContext="{Binding Source={StaticResource MyViewModelLocator}, Path=Main}"&gHow about a custom markup extension[^]?
public class LocateViewModelExtension : MarkupExtension
{
public Type ViewType { get; set; }private Type ResolveViewType(IServiceProvider serviceProvider) { if (ViewType != null) return ViewType; var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider)); return rootObjectProvider?.RootObject?.GetType(); } public override object ProvideValue(IServiceProvider serviceProvider) { Type viewType = ResolveViewType(serviceProvider); if (viewType is null) return null; if (App.ViewModelLocator is null) return null; return App.ViewModelLocator.Get(viewType); }
}
Usage:
DataContext="{LocateViewModel ViewType={x:Type my:MainView}}"
or simply:
DataContext="{LocateViewModel}"
If you don't specify a
ViewType
, it should be able to use the type of the root object for the XAML file it's used in.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
How about a custom markup extension[^]?
public class LocateViewModelExtension : MarkupExtension
{
public Type ViewType { get; set; }private Type ResolveViewType(IServiceProvider serviceProvider) { if (ViewType != null) return ViewType; var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider)); return rootObjectProvider?.RootObject?.GetType(); } public override object ProvideValue(IServiceProvider serviceProvider) { Type viewType = ResolveViewType(serviceProvider); if (viewType is null) return null; if (App.ViewModelLocator is null) return null; return App.ViewModelLocator.Get(viewType); }
}
Usage:
DataContext="{LocateViewModel ViewType={x:Type my:MainView}}"
or simply:
DataContext="{LocateViewModel}"
If you don't specify a
ViewType
, it should be able to use the type of the root object for the XAML file it's used in.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Thank you for both your answers. Working great now.
If it's not broken, fix it until it is. Everything makes sense in someone's mind. Ya can't fix stupid.