WPF 中嵌套用户控件之间的复杂命令数据绑定

Complex Command DataBinding between nested UserControls in WPF

我的 TestAdmin Class 库中有一个名为 PAInstructionsControl 的用户控件。

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

在此 UserControl 的使用者中:我需要一个 ViewModel 用作 Button 的命令绑定,并需要一个不同的 ViewModel 用于其 CommandParameter,但我不确定如何执行此操作。

在我的 WMT Class 库中,我有一个 WmtPAInstructionsView UserControl 使用 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>

WmtPAInstructionsView 嵌套在 WMT.WmtMainWindow 用户控件中

如果 Button 只是在 WmtPAInstructionsView UserControl 内,那么我需要这些 ViewModel 绑定:

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

因此该命令将绑定到 WmtMainWindow 的 DataContext 中的命令。 CommandParameter 将是 WmtPAInstructionView 的 DataContext,即 WmtPAInstructionsViewModel

但我需要 PAInstructionsControl 中的按钮,因此它可以是 re-used 我解决方案中的其他地方。我将使用什么来绑定 Command 和 CommandParameter?我需要依赖属性吗?


希望以上信息能帮助您回答我的问题。如果您需要,这里有更多信息:


我还有一个 MSVT Class 库,它有一个 Ms​​vtPAInstructionsView 使用 PAInstructionsControl 的用户控件 以类似的方式。

如果 Button 只是在 Ms​​vtPAInstructionsView UserControl 内,那么我需要这些 ViewModel 绑定:

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

WMTMSVT Class 库都引用了 TestAdmin 库. WMTMVST 不相互引用。

这是主应用程序 Window (WpfTestBase.MainWindow):

单击“新 WMT 测试”将打开一个新的 WpfTestBase.TestView Window。 这个 TestView Window 有几个嵌套的用户控件:

这是视觉树:

“简单”(但有点糟糕)的解决方案是:

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

这适用于 Window 任何 子类。虽然它确实“解决”了您的问题,但它做出了许多假设。最大的问题是你会假设你总是希望你的 Button 绑定到父 Window,并且 WindowDataContext 总是有一个属性 完全命名为 DisplayNextIndexedViewCommand.


更好的解决方案是在每个 UserControl 中定义一个 DependencyProperty 链,您可以在其中将每个子项绑定到其父项。这将使您在使用控件时具有完全的灵活性。

PAInstructionsControl开始定义一个DisplayNextIndexedViewCommandDependencyProperty。然后,您可以像这样将 Buton.Command 绑定到那个 属性:

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

这使您的嵌套更接近顶部。接下来,还要在WmtPAInstructionsView中声明一个DisplayNextIndexedViewCommandDependencyProperty。这使您可以像这样将 PAInstructionsControl.DisplayNextIndexedViewCommand 绑定到 WmtPAInstructionsView.DisplayNextIndexedViewCommand

<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>

现在,您终于可以将 WmtPAInstructionsView.DisplayNextIndexedViewCommand 绑定到 实际 命令,您在 WindowDataContext 中拥有该命令:

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

(此外,由于 DataContext 是继承的,您可以从 Binding 中省略“DataContext.”和整个 RelativeSource