How to launch multiple independent windows using a DialogService in MVVM? - c#

In MVVM fashion, I have an initial window opening (MainView) with its DataContext (i.e., its viewmodel) being initialized from the code-behind:
XAML:
<Window x:Class="Nova5.UI.Views.MainView" ..............
Code-Behind C#:
public partial class MainView : Window
{
private MainViewModel vm = new MainViewModel(new WPFMessageBoxService());
public MainView()
{
InitializeComponent();
this.Loaded += (s, e) => { this.DataContext = this.vm; };
}
}
From there on out, I use a DialogService to open other windows from UserControls:
XAML:
<Window x:Class="Nova5.UI.Views.WindowDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowDialog"
WindowStyle="SingleBorderWindow"
WindowStartupLocation="Manual" SizeToContent="WidthAndHeight">
<ContentPresenter x:Name="DialogPresenter" Content="{Binding .}"/>
</Window>
The ViewModel creates the WindowDialog and associated viewmodel and opens the window.
All is good.
Now the problem. Lets say I have ten usercontrols. Each of these UserControls can be presented by the DialogService. Each of these UserControls can open other UserControls.
What I would like to do is have the MainWindow open UserControl_1. Then have UserControl_1 open UserControl_2. UserControl_1 then needs to close leaving UserControl_2 open. Ofcourse, UserControl_2 can open UserControl_3 after which UserControl_2 disappears leaving only the MainWindow and UserControl_3. In short, each launched UserControl needs to be independent of the control that created it.
I hope this is understandable.
How can this be done? Thanks for any ideas or help.

Typically what you would need to implement here is Dependency Injection to achieve an Inversion of Control pattern. Unity is a good example of an IoC Container to use with WPF (and recommended as part of Prism).
A Dependency Injection container is essentially an in-memory container for items like Service objects or for providing implementation classes based on interfaces which you configure (either through code or configuration XML or other means) and resolve at runtime.
Your ViewModels for the various Window/Usercontrol objects would be injected with a reference to the DialogService object at construction time. The DialogService object may be maintaining a list of the relevant dialogs as they are shown and can co-ordinate the showing/closing of the Windows as necessary based on the calls/logic you code into it.
You could also code the DialogService as a Singleton pattern and achieve similar results as many in-memory Service layer implementations used in Unity-style composite applications are essentially Singletons enforced through the UnityContainer.

Related

WPF set DataContext to a ViewModel without default constructor

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.

NullReferenceException in SimpleInjector.Container.Verify when instancing view via XAML

I'm still in the process of getting into WPF and also decided to give Simple Injector a spin. I created a very simple test project based on the WPF integration example provided in the Simple Injector documentation. My current code is pretty much identical to that example or arguably even simpler as I do not yet have any service objects.
What is different is that I do have a simple View with an accompanying ViewModel that is currently the only thing that is on the MainWindow:
<Window x:Class="WpfPlayground.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfPlayground"
xmlns:view="clr-namespace:WpfPlayground.View"
mc:Ignorable="d"
Title="MyApp" Height="450" Width="800">
<Grid>
<view:MyView />
</Grid>
</Window>
My MainWindowViewModel class exists but is currently still empty. Its constructor has no parameters.
This is what I have so far of MyView (the XAML is still just an empty <Grid />):
public partial class MyView : UserControl
{
public MyView(MyViewModel myViewModel)
{
InitializeComponent();
DataContext = myViewModel;
}
}
This is the Bootstrap() method from my Program class:
private static Container Bootstrap()
{
var container = new Container();
container.Register<MainWindow>();
container.Register<MainWindowViewModel>();
container.Register<MyView>();
container.Register<MyViewModel>();
container.Verify();
return container;
}
Now, I get a NullReferenceException inside the MainWindow constructor when InitializeComponent() is called ("Object reference not set to an instance of an object"). The call stack points back to the call to container.Verify() in Program.Bootstrap() (see above). The constructors of neither MyViewModel nor MyView are ever reached.
If I were to take a guess I'd say that the application is not going through the Container to get the instance of MyView. One of the reasons I did this experiment was actually because I was interested to see how Simple Injector would accomplish this as the documentation seemed to imply that this would somehow happen automatically. Could it be there is a step missing from the documentation? Something to register some sort of interceptor? Is this maybe not the right way to use Simple Injector with WPF/XAML after all? Hopefully I won't have to create controls in code?
If I were to take a guess I'd say that the application is not going through the Container to get the instance of MyView
Yes, SimpleInjector is not able to resolve a view that you define inline in the XAML markup like this.
When the InitializeComponent() method is called at runtime, the built-in parser tries to create an instance of the view that you have defined in your XAML markup wihout any knowledge about or reference to any container.
So your code fails when trying to resolve an instance of the MainWindow, because an exception is thrown from the call to InitializeComponent() in its constructor.
The example in the docs that you refer to doesn't define any inline views. This only works for views that have a parameterless constructor.

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

Design pattern in WPF

I am making my first WPF application, so this question may seem rather odd. I have been reading about MVVM and so far it has made sense to me. What I don't understand, though, is separating all the XAML.
What I mean is this: I assume you don't place everything in the MainWindow.xaml and just collapse controls based upon what is going to be used. I would think you would want a container that would contain xaml of other files. Is this correct?
How do you go about separating the XAML out so that it isn't just a mash of everything in one file?
How do you go about separating the XAML out so that it isn't just a mash of everything in one file?
There are many ways, including creating a seperate UserControl, CustomControl, Page or Window
For example, if you wanted to pull some XAML out of your MainWindow.xaml, you could create a UserControl (right-click project, Add, New Item..., User Control (WPF)) called MyUserControl.xaml like this:
<UserControl x:Class="WpfApplication1.MyUserControl"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid>
<TextBlock>This is from a different XAML file.</TextBlock>
</Grid>
</UserControl>
and then use this control in your MainWindow.xaml like this:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myControls="clr-namespace:WpfApplication1">
<Grid>
<myControls:MyUserControl/>
</Grid>
</Window>
Note that you need to add a reference to the namespace of your UserControl
xmlns:myControls="clr-namespace:WpfApplication1"
I agree with Kevin's answer about UserControl and Window, so I'll just address the follow up question:
So I should switch between various controls that are collapsed/visible in the content of my MainWindow when the user is interacting with my application?
That is a valid option. It might get messy if you are working with a large application.
The other options that I've used are
switching Views in the code behind; i.e. on the click events you can add and remove elements from the page as you would have done in WinForms. This is not pure MVVM and some people will jump down your throat, but I believe MVVM is a tool, not a religion.
provide Views as properties in your ViewModel, and bind to them from your parent View. I don't know if this is pure MVVM, it's nice when you need to dynamically create Views depending on complex conditions but it can get complicated
use DataTemplates, which are essentially rules to determine the View to use based on the type of data that is provided. So if the data is an Address (or AddressViewModel), use the AddressView. If the data is a CustomerViewModel, use the CustomerView. And so on.
DataTemplates are the preferred pattern in my opinion - clean, easy to maintain, and a nice standard. It's trivial to go to the DataTemplate to see how the binding works, whereas the other two options I've given may lead to spaghetti code in the wrong hands.
MSDN has a nice page on DataTemplates in WPF.
When using Caliburn framework you could compose your application using smaller Views and ViewModels and have a shell which binds all those smaller views together. The shell would display one or many views at the same time, depending on how you want your application to behave. The good thing about this - unlike the pattern mentioned above where you hardcode the name of the View/UserControl in other places - is that you just create a ContentControl and bind it to the correct ViewModel property and Caliburn will find the correct View for you by convention.
Let's say you have a ShellViewModel and a ShellView which is just an empty window, and another View/ViewModel where you want to display in your shell at one point. You no longer need to instantiate your views anywhere and just work your way using POCO ViewModels objects:
public class ShellViewModel : Screen
{
public ChildViewModel Child { get; set; }
public void SomeAction()
{
Child = new ChildViewModel(); //Or inject using factory, IoC, etc.
}
}
public class ShellView : Window
{
}
<Window x:Class="WpfApplication1.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<ContentControl cal:View.Model="{Binding Child}" />
</Grid>
</Window>

Categories

Resources