将命令绑定到模板化用户控件
本文关键字:用户 控件 命令 绑定 | 更新日期: 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}}}
的东西回到可视化树,但它有点脏。