How do I specify DataContext (ViewModel) type to get design-time binding checking in XAML editor without creating a ViewModel object?

C#WpfXamlMvvm

C# Problem Overview


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}.

C# Solutions


Solution 1 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWpfNewbieView Question on Stackoverflow
Solution 1 - C#TsengView Answer on Stackoverflow