Accesing properties in a UserControl from the Main

2019-07-16 01:09发布

问题:

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

回答1:

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>


回答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.



回答3:

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