WPF set DataContext to a ViewModel without default constructor - c#

I have a ViewModel that accepts several constructor parameters. As I understand because of this the only way to set View's DataContext is using code behind.
This has its downsides:
Visual Studio will not show ViewModel inteliscence for the view that is being constructed
There is no way to see design time data that is defined in my ViewModel constructor in XAML designer because designer simply breaks
What are my options?
I would like to have a ViewModel that can accept constructor parameters, have design time data and that my Visual Studio inteliscence provide me suggestions about members in my ViewModel so I could have a good design experience.
PS. I'm using MVVM Toolkit / Windows Community Toolkit from Microsoft, but I would appreciate any answer on how to achieve my end goal. Thank you.

What are my options?
A common approach is to create a separate design time view model class with a parameterless constructor and set the design time data context to this type:
<Window ... d:DataContext="{d:DesignInstance Type=local:DesignTimeViewModel, IsDesignTimeCreatable=True}"
What you shouldn't do is to design your application and define your classes according to how the designer in Visual Studio works.

The cleanest way to assign a view-model with a paramterised constructor as the data context for a view is using the ViewModelLocator pattern.
public class ViewModelLocator
{
public ViewModelLocator()
{
// define view-model definitions in IoC container.
}
public MainViewModel MainViewModel
{
get
{
// use the appropriate construct for your choice of IoC container
var result = IoCContainer.GetInstance<MainViewModel>();
return result;
}
}
}
An instance of this class can be created in App.xaml
<Application.Resources>
<local:ViewModelLocator x:Key="ViewModelLocator">
</Application.Resources>
which becomes an application wide resource, that can be referenced in each view.
Now we can fetch an instance of a specific view-model at any time, including in the designer, and assign it as the DataContext of the view.
<Window
x:Class="MyApplication.MainView"
...
DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=MainViewModel}" />
For more details on the usage of this ViewModelLocator pattern, including having design-time data in your ViewModel, check out my blog post.

Related

MVVM and WPF structure

I've been recently trying to re-write a WinForm application to WPF. I've been trying to implement an MVVM structure into my application because it's starting to look a lot like Winforms where I need to name my controls x:Name and referencing them all the time. Essentially, I'm not using the power of what MVVM provides.
One thing I'm having trouble wrapping my head around, is the Window. Every time I create a Window, it generates a partial class. My question is, how does that tie in to MVVM? The confusion starts as to what content this class should contain. Does it have a single DataContext binding? What about button events?
From my understanding (at the moment), is that this "partial" class should have very minimal code, perhaps only to bind your ViewModel in your constructor:
this.DataContext = new ViewModel();
and the rest of the functionality should come from your ViewModel with the help of binding things on XAML. However, each ViewModel should be tied to a single Model. But what happens when this particular Window calls for many Models, such as a Client, Products, etc? Do you make a single ViewModel class that somehow does everything?
The essence of my question lies within the contents of this particual "partial" class and its relation to the ViewModel.
Looks like you've got two questions:
How the heck does this partial class thing fit in with MVVM?
How do I structure all the view models and models and stuff?
1) What you are referring to as a "partial class" is often called "code-behind" when discussing WPF. This is because in that in non MVVM patterns it usually has all the actual C# code that sits behind the xaml layout - i.e. "code-behind".
You're correct that a good indication of a good MVVM implementation is minimal/no code-behind. As you mentioned, usually all it will have is binding the DataContext to the ViewModel - and in lots of frameworks this is all handled for you and you don't even need that. In my MVVM projects every partial class looks like this:
namespace MyApp.Views
{
public partial class GeneratorView : CreatableView
{
public GeneratorView()
{
InitializeComponent();
}
}
}
Part of the nice thing with MVVM is that all the spaghetti event updating/connection code that you had to have in WinForms is all bypassed by using bindings. But as you noted, you can still do this with WPF but it's generally considered bad practice, and definitely with MVVM.
With all content and commands being bound directly to the ViewModel through data binding, the partial class need not have any content at all. There's lots of interpretations, but on a basic level:
The View lays things out and shows things on the screen.
The View Model contains UI specific data and logic. It deals with commands from the view, and may utilize business services.
The Model represents your data. Think of the stuff that's going in a database or file system.
2) MVVM is not strict in how you structure it. Generally the convention is you have ViewModel for every View. However, I believe it's fine to have a ViewModel without a View. Especially simple ones.
But what happens when this particular Window calls for many Models, such as a Client, Products, etc? Do you make a single ViewModel class that somehow does everything?
Not at all, it seems like you've got this all-or-nothing monolithic ViewModel in your head. If these were really simple structures, I'd do it like this:
However, you might want a dedicated ClientView or ProductView, and instead embed them in the main view:
The key thing is your ViewModels may contains other ViewModels, arrays of ViewModels. Similarly, your View can embed other View's to display it's ViewModels - or not. If they're simple, or you're just, say, listing a few properties (Maybe when you click an 'info' button a dialog shows up and that has the full View for said ViewModel, but in the list you just want the Name and Cost.
It's flexible. On thing is, often the Window isn't even part of the MVVM pattern. It's so "dumb" that it doesn't even have a ViewModel (also, what if you want to embed your app into another app or something?).
It's more flexible to have a high-level "AppViewModel" and all your Window does is contain that (often not even bothering with a WindowViewModel, it's not really a concern).
You can use more than one model in a view model. The aim of the view model is to abstract from the business layer (models, services).
For creating and keeping the instances you may use IoC (Inversion of Control) containers. There are many IoC containers available to use in .NET applications such as Castle Windsor, Autofac and so on (see List of .NET Dependency Injection Containers (IOC)). You just need to instantiate a view model object by necessary model objects, for example like this:
public class ViewModel
{
private readonly IClientModel _clientModel;
private readonly IProductModel _productModel;
public ViewModel(IClientModel clientModel, IProductModel productModel)
{
_clientModel = clientModel;
_productModel = productModel;
}
// Logic of your view model
}
Also you need to configure object dependencies and scopes (your model will be a singleton or a new instance of the type). The container injects dependencies when it creates the objects.
Also I recommend to read the article MVVM - IOC Containers and MVVM.
Yes, the way I approach it anyway, is to keep my Window dumb. How dumb? I guess it depends on the application. If I am just trying to throw a proof of concept or something with low importance together then I will cut a few corners. If I am working an a large application that will need to be maintained then I am going to be more strict, I may even frown on setting the ViewModel from the constructor in that case.
However, each ViewModel should be tied to a single Model
I don't know that I agree with that. That is not how I have approached MVVM, anyway. I would say that every View should be tied to a ViewModel. Within the ViewModel it may be the case that you are only dealing with one model but I have also had great success using a single ViewModel to expose multiple models to a View in a coherent way as well.
Here is an example of a template I use as a jumping off point in some of my smaller projects. I like to use explicit ViewModel properties in my Window and Views but you don't have to; you could modify this to use the DataContext property instead.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public object ViewModel
{
get { return (object)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
"ViewModel",
typeof(object),
typeof(MainWindow));
}
In the code behind the ViewModel propoerty is just a dependency property of the Window. I will bind the Window content to this property. In this case it is an object but it could be some base ViewModel class or an interface if you want.
In my Window's markup I add a DataTemplate for each ViewModel to the Window's resources. If everything is wired together correctly WPF's implicit data templating will take over and make sure the correct view is rendered whenever the ViewModel property is changed.
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:Example.ViewModels;assembly=Example"
xmlns:views="clr-namespace:Example.Views;assembly=Example">
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModels:FirstViewModel}">
<views:FirstView ViewModel="{Binding }" />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:SecondViewModel}">
<views:SecondView ViewModel="{Binding }" />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{Binding ViewModel, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
</Grid>
</Window>

ViewModel instatiated twice with MEF

I'm trying to create a simple modular MVVM application with MEF. I have a ViewModel class and a UserControl as the View. I connect the two through a DataTemplate, like this:
<DataTemplate DataType="{x:Type local:MyViewModel}">
<local:MyView />
</DataTemplate>
In the View, I define the ViewModel as a StaticResource, to make binding simple:
<UserControl.Resources>
<local:MyViewModel x:Key="ViewModel" />
</UserControl.Resources>
Then I bind like this:
<Grid DataContext="{StaticResource ResourceKey=ViewModel}">
<TextBlock Text="{Binding Text}" />
</Grid>
This all works as intended without MEF. However, as I am aiming for modularity, I use MEF to discover my ViewModel classes. I have an Export attribute on my ViewModel class:
[Export(typeof(MyViewModel))]
public class MyViewModel
{
// ...
}
and I use MEF to dynamically load the ViewModel into my shell in App.xaml.cs:
private void Application_Startup(object sender, StartupEventArgs e)
{
var shell = new MainWindow();
var catalog = new AssemblyCatalog(this.GetType().Assembly);
var container = new CompositionContainer(catalog);
shell.Contents.ViewModel = container.GetExportedValues<MyViewModel>().First();
shell.Show();
}
Now, at this point, MEF creates an instance of my ViewModel when it loads the vm, and my View creates another instance when it declares the vm as a resource. (This is easily checked by setting a breakpoint in the constructor.)
The question is, how should I pass the instance created by MEF to my resource declaration? Can I declare that specific instance as resource?
DropBox link with full code:
https://www.dropbox.com/sh/pbdl029d26sx7gl/AAA6po50dLjbJSoNPBhCyWZ3a?dl=0
The creation of the MyViewModel is purely based on the sequence of how your program execution but you can set the CreationPolicy to make your instance become a singleton so that both your code and the resources both referring to the same instance.
[Export(typeof(MyViewModel)), PartCreationPolicy(CreationPolicy.Shared)]
Side note: To use MEF, Microsoft hide the implementation of CompositionInitializer and CompositionHost from .Net Framework for some reason. Try to google up and import the 2 classes from Microsoft instead of using the CompositionContainer directly. You'll have better experience in using MEF with those.
Okay, so, what I had to do was this.
I had two instances because once MEF instantiated the ViewModels when it imported them, and then another time WPF created them, when it created the ViewModel resource. I figured that the solution would be not directly creating the resource, but had no idea how I could manage to do that. Then along came Resource Injection and then DataContextSpy, from this question here:
https://stackoverflow.com/a/5402653/5219911
And here's the direct link to the topic:
http://www.codeproject.com/Articles/27432/Artificial-Inheritance-Contexts-in-WPF
I am now using a resource that is a DataContextSpy, through which I can reach out to the ViewModel instance that is used when creating the DataTemplate.
In my View's resources, I define
and then I just set the root element's DataContext to this resource:
DataContext="{Binding Source={StaticResource ResourceKey=ViewModel},
Path=DataContext}"
Now, unfortunately, I don't get Intellisense support with this alone, as DataContextSpy is sort of a proxy to the real DataContext, so I do have to manually set up the design time DataContext type by using:
d:DataContext="{d:DesignInstance Type=viewModel:MyViewModel}"

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

This question already has answers here:
What do I need to further qualify the DataContext for a binding?
(2 answers)
Closed 7 years ago.
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}.
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.

Multiple views binding to multiple ViewModels

I want to create multiple instances of a SearchTagView and bind each of those to its own SearchTagViewModel.
I'm using the WAF framework for WPF which follows the MVVM-concept and furthermore Dependency injection (IoC). The application is developed for the SUR40 using the SurfaceSDK.
This means that the views are instantiated like this:
TagVisualizationDefinition tagDefinition = new TagVisualizationDefinition();
tagDefinition.Source = new Uri("Views/SearchTagView.xaml", UriKind.Relative);
tagVisualizer.Definitions.Add(tagDefinition);
tagVisualizer is a control element of type TagVisualizer in SearchView. So multiple SearchTagViews are placed in one SearchView. This works.
The problem is that because of dependency injection all SearchTagViews use the same SearchTagViewModel:
xmlns:vm="clr-namespace:Applications.ViewModels;assembly=Applications"
How can I use a different ViewModel for each View following the MVVM?
Every IoC framework that I've seen allows you to register types in two ways:
As a single instance (the same instance of the class is returned
each time)
As a new instance each time it is resolved / injected
You'll need to figure out how to do #2 within your IoC framework.
The other MVVM-centric option is to declare a DataTemplate in your UI and add the ViewModels directly to the UI and have WPF automatically create the Views for it.
edit:
It looks like WAF is using MEF for IoC. You'll need to supply the [Export] attribute and specify it as non-shared.
Here's how to set a PartCreationPolicy with MEF from a previous question
You can use a view model locator for this purpose. Check out this.
Configure the view model locator to return a new instance of view model every time.
An example is given below, using mvvm light(even though you need not use mvvm light to use view model locator).
public class ViewModelLocator
{
public ViewModel1 VM1
{
get
{
return new ViewModel1();
}
}
}
In app.xaml, define a key for view model locator.If you nuget mvvmlight, this will happen automatically
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:WpfApplicationmvvmlight.ViewModel" />
</Application.Resources>
In the view, use the locator
<UserControl x:Class="WpfApplicationmvvmlight.View2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding Source={StaticResource Locator}, Path=VM1}">
</UserControl>
Alan's hint concerning the non-shared attribute was good, but I couldn't use it to solve my problem. The problem was that the MEF is working before I init my TagVisualizationDefinitions.
The only solution was to set the binding in code-behind of the parent user control in the method for the event TagVisualization_Loaded

Question about ViewModel Management (DesignTime Vs Run Time)

I have a fairly basic WPF UI whereby user requests cause a new tab to open in my TabControl. The TabControl is bound to an ObservableCollection<ViewModelBase>
I add ViewModel instances to this collection, and the corresponding tab's content is displayed based on templates like this:
<DataTemplate DataType="{x:Type viewModels:UserUploadsViewModel}">
<userControls:UserUploads />
</DataTemplate>
Now let's say that inside of the UserUploads control I'd like to wire up a ViewModel in XAML to help with the designing, like this:
<UserControl x:Class=".....UserUploads"
.....
DataContext="{Binding Source={StaticResource ViewModelLocater},
Path=UserAdministrationViewModel}">
This property will return a ViewModel with live services at runtime, and a ViewModel with mock data at design time.
Question: Will this XAML interfere with what I'm doing in binding a TabItems content to a ViewModel instance, and relying on the dataTemplate above to render the right View? If so, is there a way to get both of these concepts to work together?
There is an easier way to do this. Have a DesignTimeUserAdministrationViewModel and populate it with static data in the constructor and refer that in UserControl as:
<UserControl d:DataContext="{d:DesignInstance designTimeVMs:DesignTimeUserAdministrationViewModel, IsDesignTimeCreatable=True}">
This way you have a design time test data bound to d:DataContext and runtime live data bound to the actual DataContext. More details here.
Yes I think it will interfere with your current setup
The ViewModelLocator is a static class that returns a dummy object at design time, and a static ViewModel at runtime. This means that
The ViewModelLocator, not your ParentViewModel, contains your TabViewModels
You cannot have multiple instances of the same Tab (ViewModel) open at once
You cannot manage Open/Closed tabs unless you reference the UserControl, which is a violation of the MVVM principle where the ViewModel doesn't know of the View
You can't instantiate new copies of the TabViewModel with parameterized constructors. For example, OpenTabs.Add(new CustomerViewModel(CustomerId));
Perhaps an alternative could be a Converter? One that returns a static object if in design time, or the bound object during runtime? I've never tested such a thing but in theory it should work :)
The built in MS stuff is not bad, but another more elegant and structurally sound alternative which I am busy incorporating in my project is:
http://msdn.microsoft.com/en-us/magazine/dn169081.aspx
Basically, you use the MVVM Light toolkit with the SimpleIoc container it comes with and end up with the ability to serve up data for the following three scenarios:
Design time, Run time, and Test time.
Better still, the whole point of MVVM Light is to have your stuff be directly editable in Blend and there is a whole series of videos and blogs and sample apps describing it all. I wish I had found these earlier in my WPF explorations.

Categories

Resources