简体   繁体   中英

Complex Command DataBinding between nested UserControls in WPF

I have a UserControl called PAInstructionsControl inside my TestAdmin Class Library.

<UserControl x:Class="TestAdmin.Controls.PAInstructionsControl">
        <Button Command="{Binding ???}"
                CommandParameter="{Binding ???}"/>
</UserControl>

In the consumers of this UserControl: I need a ViewModel used as the Button's Command Binding and a different ViewModel for its CommandParameter but I'm not sure how to do this.

In my WMT Class Library I have a WmtPAInstructionsView UserControl that consumes the PAInstructionsControl

<UserControl x:Class="WMT.View.WmtPAInstructionsView"
             xmlns:controls="clr-namespace:TestAdmin.Controls;assembly=TestAdmin">
    <StackPanel>
        <TextBlock Text="WMT PA Instructions View"
                   Margin="10"/>

        <controls:PAInstructionsControl />

    </StackPanel>
</UserControl>

The WmtPAInstructionsView is nested inside a WMT.WmtMainWindow UserControl WMT

If the Button was simply inside the WmtPAInstructionsView UserControl , then I would need these ViewModel bindings:

<Button 
    Command="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource=
                {RelativeSource AncestorType={x:Type local:WmtMainWindow}}, Mode=OneWay}"
    CommandParameter="{Binding}"/>

So the command would bind to a command within the WmtMainWindow 's DataContext. The CommandParameter would be the WmtPAInstructionView 's DataContext which is the WmtPAInstructionsViewModel

But I need the button in the PAInstructionsControl so it can be re-used elsewhere in my solution. What would I use for the bindings of the Command and CommandParameter? Do I need Dependency Properties?


Hopefully the above information is all you need to help answer my question. Here is more information if you need it:


I also have a MSVT Class Library that has a MsvtPAInstructionsView UserControl that consumes the PAInstructionsControl in a similar way.

If the Button was simply inside the MsvtPAInstructionsView UserControl , then I would need these ViewModel bindings:

<Button 
    Command="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource=
                {RelativeSource AncestorType={x:Type local:MsvtMainWindow}}, Mode=OneWay}"
    CommandParameter="{Binding}"/>

Both the WMT and MSVT Class Libraries reference the TestAdmin library. WMT and MVST do NOT reference each other.

Here is the Main Application Window (WpfTestBase.MainWindow):

主应用程序窗口

Clicking on "New WMT Test" opens a new WpfTestBase.TestView Window. This TestView Window has several nested user controls: WMT

Here's the Visual Tree:

视觉树

The "easy" (and sort of bad) solution would be to do this:

<Button 
    Command="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource=
                {RelativeSource AncestorType={x:Type Window}}, Mode=OneWay}"
    CommandParameter="{Binding}"/>

This would work for any subclass of Window . While it does "solve" your problem, it makes a number of assumptions. The biggest is that you'd be assuming you'll always want your Button to bind to the parent Window , and that that Window 's DataContext will always have a property exactly named DisplayNextIndexedViewCommand .


A better solution would be to define a chain of DependencyProperty s in each of your UserControl s, where you would bind each child to its parent. This would give you total flexibility when consuming the controls.

Start with PAInstructionsControl and define a DisplayNextIndexedViewCommand DependencyProperty . You would then bind Buton.Command to that property like so:

<Button 
    Command="{Binding DisplayNextIndexedViewCommand, RelativeSource=
                {RelativeSource AncestorType={x:Type local:PAInstructionsControl}}, Mode=OneWay}"
    CommandParameter="{Binding}"/>

This brings you one level of nesting closer to the top. Next, also declare a DisplayNextIndexedViewCommand DependencyProperty in WmtPAInstructionsView . This lets you bind PAInstructionsControl.DisplayNextIndexedViewCommand to WmtPAInstructionsView.DisplayNextIndexedViewCommand like so:

<UserControl x:Class="WMT.View.WmtPAInstructionsView"
             xmlns:controls="clr-namespace:TestAdmin.Controls;assembly=TestAdmin">
    <StackPanel>
        <TextBlock Text="WMT PA Instructions View"
                   Margin="10"/>

        <controls:PAInstructionsControl DisplayNextIndexedViewCommand="{Binding DisplayNextIndexedViewCommand, RelativeSource={RelativeSource AncestorType={x:Type local:WmtPAInstructionsView}}}"/>

    </StackPanel>
</UserControl>

Now, finally, you can bind WmtPAInstructionsView.DisplayNextIndexedViewCommand to the actual command, which you have living in your Window 's DataContext :

<local:WmtPAInstructionsView DisplayNextIndexedViewCommand="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource={RelativeSource AncestorType={x:Type local:WmtMainWindow}}, Mode=OneWay}"/>

(Also, since DataContext is inherited, you might be able to leave out " DataContext. " and the whole RelativeSource from the Binding )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM