I have a little problem and I'm looking the correct way to implement it.
I have a MainWindow and a UserControl to display some result, in the MainWindow I have a button "load" to load some data and the UserControl should display them.
I'm not sure what is the correct way to do this in WPF and MVVM:
Should I pass the MainWindowModel to the UserControlModel?;
Should I pass the UserControlModel to the MainWindowModel?;
Should I expose the property I need to fill as DependencyProperty in my UserControl and then fill it on the MainWindow?
Any suggestion would be appreciated. Thanks!
Edit 1:
This is how I call my UserControl:
<TabControl Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="0"
VerticalAlignment="Stretch">
<!--Result View-->
<TabItem Header="{Binding TabImportHeader}">
<results:ResultView/>
</TabItem>
<!--Configuration Tab-->
<TabItem Header="{Binding TabConfigurationHeader}">
<configuration:ConfigurationView />
</TabItem>
</TabControl>
My UserControl where my problem appear is the ResultView
MainWindowVMInstance.UserControlVMInstance.Property
The UserControl
is inside your MainWindow
.
Therefore your MainWindow
has a property (/instance) of your UserControlVM
.
Note: If you also need a reference of MainWindowVM
inside your UserControlVM
, pass it to the constructor and store it as property.
In xaml it would look like this (inside MainWindow.xaml
):
<ContentControl Content="{Binding UserControlVMInstance}"/>
Don't forget the DataTemplate
:
<DataTemplate DataType="{x:Type vm:UserControlVM}">
<view:UserControlView/>
</DataTemplate>
Edit after question update:
This is an example with a part of your code to demonstrate WPF and MVVM in action. You simply define a DataTemplate
in your UserControl.Resources
and then give the ContentControl
via Binding
an instance of your UserControlVM
. WPF knows there's a DataTemplate
for this type and will add an instance of the UserControlView
where the ContentControl
is.
<MainWindow>
<MainWindow.Resources>
<DataTemplate DataType="{x:Type vm:UserControlVM}">
<view:UserControlView/>
</DataTemplate>
</MainWindow.Resources>
<!-- Your TabControl -->
<TabControl>
<!--Result View-->
<TabItem Header="{Binding TabImportHeader}">
<ContentControl Content="{Binding TabImportCONTENT}"/>
</TabItem>
</TabControl>
</MainWindow>
Sharing ressources between ViewModels is a common scenario, however it is usually a good indicator of something going wrong in the architecture - or a lack thereof - if it does become a problem.
The easiest solution would be to have the UserControlVM
hold a reference to the MainWidowVM
and subscribe to a certain event which gets called by the interface via a Command
. Crossreferencing between ViewModels
which declare functionality can however become really really messy down the road with cross- and circle references eventually preventing GarbageCollection
from doing its job properly causing unwanted memory leaks.
You could also have your MainWindowVM
hold references to every other ViewModel
to provide a centralized point of communication by having crossreferences at least centralized.
The next easiest solution would be to implement something like an external messenging system with weak event binding. This gets rid of fixed references of objects within other objects but comes at the price of being extremely heavy on performance. If you are developing an app that has for instance a masterlist with elements to select and many modular detail-lists in different windows this might be feasible - oneway communication from 1 master-end with unfrequent changes and asynchronous implementation on many detail-ends. For your purpose this should be fine, if these events will be fired automatically however I suggest taking a different router entirely.
The problems listed above is why MVVM not only has Model
s, ViewModel
s and View
s but usually also use Services
to encapsulate shared logic and/or data (resources) off from ViewModels
into what's effectively a shared resource.
Your ViewModels (and maybe views in case of a LocalizationService
for instance) will hold references to a service and usually all to a single instance of that service. This also gets rid of unwanted cross-references and allows proper binding and proper (un-)subscription from events without the performance penalty of weak bindings.
The other upside to using Services
instead of crossreferencing ViewModels
is that it does give you the oppurtunity of actually following the SOLID principles of object-oriented programming and having the logic in the place it belongs without duplication.
Following that one could use an IoC-Container to properly inject references and dependencies at runtime, this however comes at the risk of hidden dependencies and a hell of a lot of preparation time with project setup, documentation, etc. if done improperly and/or for the first time. I personally wouldn't going this route unless every member of the team actually knows what they are doing entirely.
Microsoft WPF controls are build with DependencyProperties
DependencyProperty supports the following capabilities, which you are loosing when introduce control specific view model:
DP can be set in a style / trigger (styling and templating are important, developers often want to reuse functionality of a control, but change apperance completely)
DP can be set through data binding (that includes cross-binding between 2 controls)
DP can inherit its value automatically from a parent element in the element tree
DP can be animated