将命令绑定到模板化用户控件

本文关键字:用户 控件 命令 绑定 | 更新日期: 2023-09-27 18:32:42

我是WPF的新手,但由于有一本关于该主题的好书,当然还有像这样的网站上的高质量帖子,我能够在短时间内取得很多进展。但是,现在我遇到了一些我似乎可以通过这些方式弄清楚的事情,所以我发布了我的第一个问题。

我在资源字典中有一个控件模板,我将其应用于多个用户控件视图。该模板提供了一个简单的叠加边框和两个按钮:"保存"和"取消"。模板化用户控件包含各种文本框等,并根据上下文绑定到某个 ViewModel。我正在尝试弄清楚当我在某些视图中使用/声明 UserControl 时如何将命令绑定到保存/取消按钮。这甚至有可能,还是我做错了什么?

首先,模板:

<ControlTemplate x:Key="OverlayEditorDialog"
                 TargetType="ContentControl">
    <Grid>
        <Border HorizontalAlignment="Stretch"
                VerticalAlignment="Stretch"
                Background="DarkGray"
                Opacity=".7"/>
        <Border HorizontalAlignment="Center" 
                VerticalAlignment="Center"
                Background="DarkGray">
                <Grid>
                   <RowDefinition Height="Auto"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>                    
                <ContentPresenter Grid.Row="0"/>                    
                <Grid Grid.Row="1" 
                      Margin="10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Button Grid.Column="1"
                            Content="Cancel"
                            ***Command="{Binding CancelCommand}}"**
                            />
                    <Button Grid.Column="0"
                            Content="Save"
                            ***Command="{Binding Path=SaveCommand}"***/>
                </Grid>
            </Grid>
        </Border>
    </Grid>
</ControlTemplate>

模板又用于客户编辑器覆盖用户控件

<UserControl x:Class="GarazhApp.View.CustomerEditorOverlay"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
<UserControl.Resources>
    <ResourceDictionary Source="Dictionary1.xaml"/>
</UserControl.Resources>
<ContentControl Template="{StaticResource ResourceKey=OverlayEditorDialog}">        
    <Grid Grid.Row="0"
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>                
        </Grid.RowDefinitions>
        <SomeElement/>
        <SomeOtherElement/>
    </Grid>
</ContentControl>    

。最后,用户控件用作视图的一部分,如下所示:

<local:CustomerEditorOverlay Visibility="{Binding Path=CustomerViewModel.ViewMode, Converter={StaticResource myConverter}, FallbackValue=Collapsed}"
                                 d:IsHidden="True" />

将命令绑定到模板化用户控件

因此,根据我从一个我从事了半年的项目中学到的东西,我们有一个可行的模式。

假设您有一堆模式窗口,它们都在应用程序中应用相同的样式。若要在每个视图上具有"保存"和"取消"按钮,用于所有模式窗口的用户控件具有多个依赖项属性。此外,我们为您的命令指定虚拟方法(例如 OnSaveCommand、OnCancelCommand、CanExecuteSaveCommand、CanExecuteCancelCommand(以及命令本身作为属性存储在由您的视图继承的基本 ViewModel 中。

最终,我们只需这样做即可创建新的模态窗口:

<my:YourBaseView x:class="MyFirstView" xmlns:whatever="whatever" [...]>
    <my:YourBaseView.PrimaryButton>
         <Button Content="Save" Command="{Binding SaveCommand}" />
    </my:YourBaseView.PrimaryButton>
    <!-- some content -->
</my:YourBaseView>

附带的代码隐藏:

public class MyFirstView : YourBaseView
{
    [Import] /* using MEF, but you can also do MvvmLight or whatever */
    public MyFirstViewModel ViewModel { /* based on datacontext */ }
}

和一个视图模型:

public class MyFirstViewModel : ViewModelBase
{
    public override OnSaveCommand(object commandParameter)
    {
        /* do something on save */
    }
}

此用户控件的模板在网格布局中指定内容控件,其中 Content 属性绑定到主按钮和辅助按钮。当然,模式的内容存储在用户控件的 Content 属性中,并显示在 ContentPresenter 中。

<Style TargetType="{x:Type my:YourBaseView}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type my:YourBaseView}">
                <Grid>
                    <!-- ignoring layout stuff -->
                    <ContentControl Content="{TemplateBinding Content}" />
                    <ContentControl Content="{TemplateBinding PrimaryButton}" />
                    <ContentControl Content="{TemplateBinding SecondaryButton}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

用户控件代码:

public class YourBaseView : UserControl
{
    public static readonly DependencyProperty PrimaryButtonProperty =
        DependencyProperty.Register("PrimaryButton", typeof(Button), typeof(YourBaseView), new PropertyMetadata(null));
    public Button PrimaryButton
    {
        get { return (Button)GetValue(PrimaryButtonProperty); }
        set { SetValue(PrimaryButtonProperty, value); }
    }  
    /* and so on */
}

当然,您可以更改模板化视图的每个实例的样式。我们只是碰巧坚持一种基本风格。

TL;DR 编辑:我可能有点过火了,因为我认为您只需要了解每次创建新覆盖时都会公开通过 XAML 设置的按钮类型的依赖项属性。或者你可以用类似{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType={x:Type MyView}}}的东西回到可视化树,但它有点脏。