2

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

Nekeniehl
  • 1,633
  • 18
  • 35
  • MainWindowVM -> Property -> UserControlVM – Blacktempel Jan 24 '18 at 13:00
  • If I understand your comment: MainWindowVM.DataToLoad => UserControlVM.DataToLoad? – Nekeniehl Jan 24 '18 at 13:02
  • 1
    `MainWindowVMInstance.UserControlVMInstance.Property` - your `UserControl` is inside the `MainWindow` correct ? So you should already have a property `UserControlVMInstance` - however you might call it - inside your `MainWindowVM`. – Blacktempel Jan 24 '18 at 13:04
  • The UserControl is inside the MainWindow, that is right, but I don't have a instance of the UserControl, because I don't really know who should have the instance. The UserControl of the MainWindow or the MainWindow of the UserControl, but I think you already said it and that it should be MainWindowVM.UserControlVM.Property. Thanks! – Nekeniehl Jan 24 '18 at 13:07

3 Answers3

2

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>
Blacktempel
  • 3,935
  • 3
  • 29
  • 53
2

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 Models, ViewModels and Views 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.

Jirajha
  • 483
  • 3
  • 10
  • I tought about the possibility to interact with the VM of my userControl with events, that is why I wasn't sure about who should have the property. Of couse I have Services for the businessLogic, it's extracted and isolated in different places, but i.e. the problem I came with is because the MainWindow contains the LoadCommand button, which goes to ImportService, load the stuff and returning it to the View to show it. And the view is now my new userControl, that is why I made the question, because I'm a complete newie in MVVM and WPF: Thanks a lot for your answer! – Nekeniehl Jan 24 '18 at 14:18
1

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

ASh
  • 34,632
  • 9
  • 60
  • 82
  • I see, so, based on your answer, it will be better to implement the userControl without MVVM in order to use it as DependencyProperties if I want to reuse the control, right? Is there no a mix solution where you have your viewModel and DependencyProperties? – Nekeniehl Jan 24 '18 at 13:45
  • @Nekeniehl, for everything UI-specific I declare DP. if you have some setting which doesn't fall into UI-specific category, then it is possible to introduce some additional class. However I can't give example when it would be absolutely necessary. If I can (almost always) I don't create VM for control – ASh Jan 24 '18 at 13:55