Reputation: 47
I am pretty new to WPF and right now I am trying to get used to the MVVM pattern. Right now I have a simple application in which I have a collection of ViewModels that I display in a grid. When I doubleclick on the row in the grid I want to show a details View of the ViewModel.
The problem I am having right now is that I already have a fully instanced ViewModel, but I can't seem to pass it into the view. When I try to load that View it turns up empty. I already found out that this is due to the fact that when a View gets loaded it creates it's own instance of the backing ViewModel. So obviously I need to get around this behaviour and somehow pass the instanced ViewModel into the View when it is created. I could use a constructor in the View that takes a ViewModel and set the datasource in there. However, taking this approach but would mean that I need to construct the View in the ViewModel and thus making the ViewModel aware of the View. This I something I would like to avoid since I am trying to uphold the MVVM pattern.
So what should I do in this case? Should I just break the MVVM pattern or are there some nice and clean sollutions for this that fit in the MVVM pattern?
Upvotes: 1
Views: 6741
Reputation: 3043
"Should I just break the MVVM pattern?"
Well, please consider to learn more about the pattern, to know what it is to "break it". The main purpose of this pattern is to keep responsability clear, thus to obtain testable and maintainable code. There are a lot of ressource for that as show in this question: MVVM: Tutorial from start to finish?
Anyway to be more specific about your question, what you are looking for is how to set the DataContext.
"somehow pass the instanced ViewModel into the View when it is created"
Yes, you get it, if you assign the dataContext with a viewModel in the constructor of your view, it could work but it it is acceptable only if the viewModel has the responsability to create the view (which could be acceptable in really few situation). You could even write something like that to directly set DataContext from outside your view:
var l_window = new MyView { DataContext = new MyViewModel() };
l_window.Show();
Of course the main drawback is that this code is not testable. If you would like to test it you should use a mockable service to manage the view creation.
A more common solution is to inject the dataContext with an IOC container (like prism). You create all required ViewModel when the software started and you store them in this IOC container. Then, when the view is created, you ask this container to get you an instance of your viewModel.
An example could be: export your viewModel in PRISM:
[Export]
public class MyViewModel {...}
And then Import it in your view:
[Import]
private MyViewModel ViewModel
{
set { this.DataContext = value; }
get { return this.DataContext as MyViewModel; }
}
Hope it helps.
Upvotes: 2
Reputation: 5728
I agree with @Sheridan's answer and would only like to add another way to instantiate a view with a view model: you could use the Factory Pattern, maybe like this:
public class ViewFactory
{
public UIElement Create(object context)
{
// Create the view model
// You can pass in various information by parameters
// as I do with context (Constructor Injection)
var viewModel = new ViewModel(context);
// Create the view and set the view model as data context
var view = new View { DataContext = viewModel };
return view;
}
}
You can call this factory from within a method of your view model and then assign it to e.g. a property that is data bound to the UI. This allows for a bit more flexibility - but @Sheridan's solution is also fine.
Upvotes: 0
Reputation: 69959
There are many ways of passing a view model to a view, as you call it, or setting a view model as the DataContext
of either a Window
or UserControl
, as others may call it. The simplest is just this:
In a view constructor:
public partial class SomeView
{
InitializeComponent();
DataContext = new SomeViewModel();
}
A more MVVM way might be to define DataTemplate
s in App.xaml
for each view model that defines which view each will use:
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourViewModel">
<YourViewsPrefix:YourView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type YourViewModelsPrefix:AnotherViewModel">
<YourViewsPrefix:AnotherView />
</DataTemplate>
Now whenever the Framework comes across an instance of these view model classes, it will render the associated view. You can display them by having a property of the type of your view model using a ContentControl
like this:
<ContentControl Content="{Binding YourViewModelProperty}" />
Or even in a collection like this:
<ListBox ItemsSource="{Binding YourViewModelCollectionProperty}" />
Upvotes: 4