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}"
Related
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.
I am trying to find a better way to declare the ViewModel a UWP XAML Page uses.
At this moment,
I declare a ViewModel class ViewModelClass that contains my data properties.
Then I declare an instance of that class as StaticResource of the Page. I like calling those VIEW for consistency across all my Page designs.
Finally, I declare the Page's DataContext as a Binding to the StaticResource VIEW.
This yields a page that understands what data structure is in use and allows AutoComplete when working Bindings. Nice, though lots of lines of same-old-same-old code.
Only, it is not really suitable to ViewModels as the declared resource is a static resource. It is instantiated when the page is instantiated. Most pages will receive a ViewModel parameter upon NavigatedTo, which cannot be used to replace the static resource, because it is, well. static.
So I end up changing the Page's DataContext upon navigation from the initial reference to VIEW to the ViewModel instance I actually want to use.
Big caveat is to declare the back-reference to the page's DataContext when deep in the bowels of a Master-Detail situation is rather horrible. Imagine a collection whose display is in part depending on a Master's property.
How do you tie back robustly to the DataContext of the page from anywhere inside the page?
I have tried giving the page a Name (PAGE for simplicity) and then using ElementName=PAGE, Path=DataContext.someProperty. Ugly, plus you lose all information of the class represented by DataContext.
Another approach is to create a Wrapper around the actual ViewModel called StaticViewModel that has only one property: public ViewModel viewModel. Now I can declare the wrapper as a StaticResource, and tell the page's top-level FrameworkElement to use VIEW.viewModel as its DataContext. Works, and reliably, but sooooo ackward and cumbersome.
I would LOVE to implement a SOURCE class for bindings called PageDataContext that would do nothing else but to loop into the page and get the DataContext from there.
Imagine: {Binding someProperty, Source={PageDataContext}
How would I go about declaring said Source class for a UWP app???
I would LOVE to implement a SOURCE class for bindings called PageDataContext that would do nothing else but to loop into the page and get the DataContext from there. Imagine: {Binding someProperty, Source={PageDataContext}
For your requriment, you could implement your viewmodel in the page Resources and give it x:key. When you bind the property of viewmodel you could access this viewmodel with x:key Source={StaticResource ViewModel} for more please refer the following code.
ViewModel
public class ViewModel
{
public ViewModel()
{
Visibility = false;
}
public bool Visibility { get; set; }
}
Xaml
<Page.Resources>
<local:ViewModel x:Key="ViewModel" />
</Page.Resources>
<TextBlock
Width="100"
Height="44"
Text="{x:Bind Name}"
Visibility="{Binding Visibility, Source={StaticResource ViewModel}}" />
Well, no dice so far.
Working with all those options I found that the best one is a Wrapper class that can be assigned to a static resource which in turn is the basis for the page's DataContext. The wrapped instance of the actual ViewModel is then assigned during OnNavigatedTo() and used as the DataContext of the basic FrameworkElement of the page.
Brr, so much verbose code.
STILL WONDERING how to implement a different version of a source directive. Is there no way to declare one's own Source for a Data Binding? Is this stuff really hard-coded into the framework???
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.
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.
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