Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

mvvm

wpf

xaml

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.

like image 235
WpfNewbie Avatar asked Apr 01 '15 19:04

WpfNewbie


People also ask

How do I bind ViewModel to XAML?

There are 3 ways to bind the View with ViewModel. One way is to bind DataContext Property within a XAML, Second is to assign DataContext within Code-Behind and last using ViewModelLocator.

What is DataContext in XAML?

The XAML in WPF is just a pretty user interface to display and interact with the actual data, otherwise known as the DataContext . The purpose of other binding sources ( RelativeSource , ElementName , etc) is to point to another property that doesn't exist in the current control's DataContext.

How many ways you can bind a ViewModel with XAML?

There are two ways in which we can bind View and View Model.


1 Answers

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.

like image 113
Tseng Avatar answered Oct 09 '22 22:10

Tseng