No, there is no built-in feature for that in WPF. That is what libraries like Prism are for, so you do not have to reinvent the wheel. In the background, Prism uses multiple mechanisms to achieve that.
- A dependency injection container to register and resolve types
- Sets to track which view belongs to which view model
- Discovery of view models via reflection and naming conventions
- An attached property to assign a view model automatically to a view.
- ...
Depending on your requirements you would have to write parts of that yourself or use alternative concepts that are more lightweight, like a view registry type and a factory to create them, for exapmle:
public interface IViewRegistry
{
void Register<TView, TViewModel>()
where TView : FrameworkElement
where TViewModel : INotifyPropertyChanged;
Type GetViewModelType<TView>()
where TView : FrameworkElement;
}
public interface IViewFactory
{
TView Create<TView>()
where TView : FrameworkElement;
}
The actual view registry implemenatation would have a dictionary to keep track of the mapping between views and view models that can is exposed by GetViewModelType. The type constraint FrameworkElement ensures that you can set a DataContext.
public class ViewRegistry : IViewRegistry
{
private Dictionary<Type, Type> _mappings;
public ViewRegistry()
{
_mappings = new Dictionary<Type, Type>();
}
public void Register<TView, TViewModel>()
where TView : FrameworkElement
where TViewModel : INotifyPropertyChanged
{
_mappings.Add(typeof(TView), typeof(TViewModel));
}
public Type GetViewModelType<TView>()
where TView : FrameworkElement
{
return _mappings.ContainsKey(typeof(TView)) ? _mappings[typeof(TView)] : null;
}
}
The factory would either create new instances of a view and its view model using reflection or via a dependency injection container and assign the DataContext. I really recommend you to use a dependency container, because your view models might have lots of dependnecies and you would have to resolve or create them yourself. See @BionicCode's comment on that.
public class ViewFactory : IViewFactory
{
private readonly IViewRegistry _viewRegistry;
public ViewFactory(IViewRegistry viewRegistry)
{
_viewRegistry = viewRegistry;
}
public TView Create<TView>() where TView : FrameworkElement
{
var viewModelType = _viewRegistry.GetViewModelType<TView>();
var view = // ...resolve the view via dependency injection or create it via reflection using its type
var viewModel = // ...resolve the view model via dependency injection or create it via reflection using its type
view.DataContext = viewModel;
return view;
}
}
In your application, the process of registering an resolving views now looks similar to Prism.
var viewRegistry = new ViewRegistry();
var viewFactory = new ViewFactory(viewRegistry);
viewRegistry.Register<MainWindow, MainWindowViewModel>();
var mainWindow = viewFactory.Create<MainWindow>();