This question already has an answer here:
-
What do I need to further qualify the DataContext for a binding?
2 answers
I can specify DataContext like this:
<Window ... >
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
...
</Window>
And in this case WPF will create an object of type MainViewModel and assign it to the window's DataContext property (this happens inside Window's InitializeComponent()
method).
But what if my ViewModel
doesn't have a default constructor. Or what if I want to initialize ViewModel and assign it to DataContext after Window.InitializeComponent()
is executed (inside Window's constructor or from the same code which instantiates the window) - in this case WPF creates a ViewModel (inside InitializeComponent()
), assigns it to window's DataContext and then I overwrite it with another instance of ViewModel (I'm worried about unnecessary object instantiation here).
I would like to be able to specify just a type of ViewModel
, so I would get design-time warning if I misspell a property name inside {Binding} (or after renaming the property), or I could Go To Declaration by clicking (in XAML) on a property name inside {Binding PropertyName}.
Thank you for your priceless time.
That's the tricky part if you do the do-it-yourself MVVM.
Your options, basically:
Use Dependency Injection
You could inject the ViewModel in your Page
/Window
's constructor and assign it within it.
This has a few downsides though.
- harder to use design-time view models
- Views can't be instantiated from XAML anymore
ViewModel First with Navigation Service
You'd resolve your ViewModels and do all your navigation via a navigation service. In your ViewModels you pass the an INavigationService
. You could navigate to a view by using ViewModel type. Inside it instantiate the ViewModel via Dependency Injection, then instantiate the View (based on naming conventions or via DI configuration)
That's a bit better, but still won't allow you to instantiate the Views within XAML. Big plus is, it allows you easily to pass parameters to the ViewModel (having the ViewModels implement INavigationAware
property with NavigatedTo
method, which is called after instantiation and passing the parameter to)
ViewModelLocator / Attached Property/Behavior
With this one, you would create an attached property, which you either set to true
(i.e. autowire) or to a ViewModel type (to have more control over the ViewModel instantiated) and the find and resolve the ViewModel and assign it.
It basically gives all of the advantages above plus instantiation form View.
Last one is basically what Microsoft's MVVM framework "Prism" does (navigation service navigationService.Navigate("MyPage", myParameterForViewModel)
, DataContext instantiation and assignment from XAML via autowireing (In XAML: prism:ViewModelLocator.AutoWireViewModel="True"
).
That being said, it's best to use an matured MVVM Framework which does this parts of your wiring (even if you decide not to use the base classes such as BindableBase
or whatever it's called in said framework).
As for design-time ViewModel/auto-completition for ViewModels:
You can use Blend's Design-Time attributes to do this. You need to add the Blend assembly references first. Then you can add xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
namespace into your page/view.
Then you can bind it into your page via d:DataContext="{d:DesignInstance my:DesignTimeViewModel, IsDesignTimeCreatable=True}
. Notice the d:
before the DataContext, this is important. This DataContext will only be used in the Designer (Visual Studio XAML Designer or in Blend). This is to prevent interfering with the normal DataContext
(without the prefix).
Example:
<Window x:Class="WpfApplication1.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:myApp="clr-namespace:WpfApplication1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance myApp:Window2ViewModel, IsDesignTimeCreatable=True}">
<Grid>
<TextBlock Text ="{Binding Test}"/>
</Grid>
</Window>
If you use Interfaces for your ViewModels, it's pretty fast to create the Design Instance, by simply having Visual Studio implement all the Interface property and give it some default values (for property so you have example data in your ViewModel to verify bindings work correctly).
This requires you to create separate design-time ViewModels and your actual ViewModels, which isn't as bad as it sounds. This gives your UI designer the chance to work with it, even if the real ViewModel isn't finished/implemented yet.