我先让一张照片来说话。
所以你看,我想创建一个支持绑定(bind)到父窗口的 DataContext 的 WPF 用户控件。用户控件只是一个 Button 和一个带有自定义 ItemTemplate 的 ListBox,以使用 Label 和 Remove Button 呈现事物。
Add 按钮应该调用主视图模型上的 ICommand 以与用户交互以选择新事物(IThing 的实例)。用户控件中 ListBoxItem 中的 Remove 按钮应该类似地调用主视图模型上的 ICommand 来请求删除相关事物。为此,Remove 按钮必须向 View 模型发送一些关于请求删除的事物的识别信息。所以有 2 种类型的 Command 应该可以绑定(bind)到这个控件。像 AddThingCommand() 和 RemoveThingCommand(ITing thing) 这样的东西。
我使用 Click 事件使该功能正常工作,但这感觉很麻烦,在 XAML 后面产生了一堆代码,并且与原始 MVVM 实现的其余部分发生冲突。我真的很想正常使用命令和 MVVM。
有足够的代码可以让一个基本的演示工作,我推迟发布整个事情以减少困惑。使我感觉如此接近的工作是 ListBox 的 DataTemplate 正确绑定(bind)了 Label,并且当父窗口将项目添加到集合中时,它们会显示出来。
<Label Content="{Binding Path=DisplayName}" />
虽然它正确显示了 IThing,但当我单击它时,它旁边的“删除”按钮什么也不做。
<Button Command="{Binding Path=RemoveItemCommand, RelativeSource={RelativeSource AncestorType={x:Type userControlCommands:ItemManager }}}">
这并不是非常意外,因为没有提供特定的项目,但是“添加”按钮不必指定任何内容,并且它也无法调用该命令。
<Button Command="{Binding Path=AddItemCommand, RelativeSource={RelativeSource AncestorType={x:Type userControlCommands:ItemManager }}}">
所以我需要的是添加按钮的“基本”修复,以便它调用父窗口的命令来添加一个东西,以及更复杂的删除按钮修复,以便它也调用父命令但也传递它绑定(bind)的东西。
非常感谢您的任何见解,
最佳答案
这是微不足道的,并且通过将您的 UserControl 视为它是一个控件(恰好由其他控件组成)来实现。这意味着什么?这意味着您应该将 DependencyProperties 放置在您的 ViewModel 可以绑定(bind)到的 UC 上,就像任何其他控件一样。按钮公开 Command 属性,TextBoxes 公开 Text 属性等。您需要在 UserControl 的表面上公开完成工作所需的一切。
让我们举一个简单的(不到两分钟的时间放在一起)的例子。我将省略 ICommand 实现。
首先,我们的窗口
<Window x:Class="UCsAndICommands.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:UCsAndICommands"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<t:ViewModel />
</Window.DataContext>
<t:ItemsEditor Items="{Binding Items}"
AddItem="{Binding AddItem}"
RemoveItem="{Binding RemoveItem}" />
</Window>
请注意,我们有我们的项目编辑器,它公开了它需要的所有内容的属性——它正在编辑的项目列表、添加新项目的命令和删除项目的命令。
接下来,用户控件
<UserControl x:Class="UCsAndICommands.ItemsEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:t="clr-namespace:UCsAndICommands"
x:Name="root">
<UserControl.Resources>
<DataTemplate DataType="{x:Type t:Item}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveItem, ElementName=root}"
CommandParameter="{Binding}">Remove</Button>
<TextBox Text="{Binding Name}" Width="100"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Button Command="{Binding AddItem, ElementName=root}">Add</Button>
<ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
</StackPanel>
</UserControl>
我们将控件绑定(bind)到 UC 表面上定义的 DP。请不要做任何像
DataContext=this;
这样的废话。因为这种反模式破坏了更复杂的 UC 实现。这是 UC 上这些属性的定义
public partial class ItemsEditor : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<Item>),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public IEnumerable<Item> Items
{
get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region AddItem
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register(
"AddItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand AddItem
{
get { return (ICommand)GetValue(AddItemProperty); }
set { SetValue(AddItemProperty, value); }
}
#endregion
#region RemoveItem
public static readonly DependencyProperty RemoveItemProperty =
DependencyProperty.Register(
"RemoveItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand RemoveItem
{
get { return (ICommand)GetValue(RemoveItemProperty); }
set { SetValue(RemoveItemProperty, value); }
}
#endregion
public ItemsEditor()
{
InitializeComponent();
}
}
只是 UC 表面上的 DP。没什么大不了的。我们的 ViewModel 同样简单
public class ViewModel
{
public ObservableCollection<Item> Items { get; private set; }
public ICommand AddItem { get; private set; }
public ICommand RemoveItem { get; private set; }
public ViewModel()
{
Items = new ObservableCollection<Item>();
AddItem = new DelegatedCommand<object>(
o => true, o => Items.Add(new Item()));
RemoveItem = new DelegatedCommand<Item>(
i => true, i => Items.Remove(i));
}
}
您正在编辑三个不同的集合,因此您可能希望公开更多 ICommand 以明确您正在添加/删除哪些。或者您可以便宜并使用 CommandParameter 来解决这个问题。
关于wpf - 如何在 UserControl 和父窗口之间绑定(bind) WPF 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29126224/