ItemsControl 中 DataTemplate 上的事件处理程序

Event Handlers on DataTemplate inside ItemsControl

我有一个 ItemsControl,这样我就可以显示同一模板的多个实例。我需要能够在事件处理程序上执行代码,以便我可以区分控件。

例如:我有一份杂货清单,所以我的 DataTemplate 包含每种食物的 "buy" Button。我想将所述按钮绑定到代码并告诉按下了哪个按钮。

考虑到我正在使用 MVVM 设计模式

,我怎样才能做到这一点

** XAML :**

<ItemsControl ItemsSource="{Binding MyItemList}">
     <ItemsControl.ItemsTemplate>
          <DataTemplate>
              <Button Content="Buy" />
          </DataTemplate> 
     </ItemsControl.ItemsTemplate>
</ItemsControl>

因此,MyItemList 是一个 List<MyItem> 实例。 DataTemplate 包含修改值或执行 MyItem 中不存在的代码的控件:

我已经阅读了很多关于命令的投标模板的文章,但我找不到使用项目列表的文章。

您可能会做几件事。

<Button Content="Add" Click={Click} Tag="{Binding .}" DataContext="{Binding .}" />

DataContext="{Binding .} - 将整个 VM 实例设置为 属性。您可以对 Tag 属性 执行相同的操作。有时将 Tag 用于这些目的很有用. 你可以使用它们中的任何一个。两者都可以。

public void Click(...)
{
    var control = sender as FrameWorkElement;
    if(control!= null)
    {
        var myVM = control.DataContext as MyViewModel;
        myVM.DoSomethingWithMyVM();
    }
}
  1. 您可以创建一个包含网格的用户控件,并在网格中引用自定义用户控件。这是非常灵活的。在它的 ButtonEventhandler 中,您可以访问数据上下文并使用它执行您需要的操作。这要容易得多,但是您将需要更多的工作来处理父对象的通知。如果您想重用此控件,这会更好。

  2. 您可以做的另一件事是将按钮的数据上下文设置为整个 ViewModel。最后一个解决方案是将按钮的标签设置为整个 ViewModel。如果您不打算重复使用它,那就更好了。

  3. 您也可以将其用作 resourceDictionary 中的资源。

您永远不应在 DataTemplate 中使用事件,这将使您使用强制转换,然后在整个 MVVM 模式中造成漏洞。按钮具有命令 属性,您应该将其 属性 绑定到 MyItem ViewModel 中的命令。

如果您仍然需要使用事件(例如,您不能将 MouseDown 绑定到命令),您应该使用 EventToCommadn 行为,它允许您将事件绑定到命令。你可以在这里阅读:http://msdn.microsoft.com/en-us/magazine/dn237302.aspx

您需要将按钮绑定到 ItemsControl 的 DataContext 的命令。

在 WPF 中搜索命令:(常见实现):

public class RelayCommand<T> : IRelayCommand
{
    private Predicate<T> _canExecute;
    private Action<T> _execute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    private void Execute(T parameter)
    {
        _execute(parameter);
    }

    private bool CanExecute(T parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public bool CanExecute(object parameter)
    {
        return parameter == null ? false : CanExecute((T)parameter);
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var temp = Volatile.Read(ref CanExecuteChanged);

        if (temp != null)
            temp(this, new EventArgs());
    }
}

在您的 ViewModel 中(ItemsControl 的 DataContext,我希望 :))

   private RelayCommand<FoodItem> _addToGroceriesCommand;
   public ICommand AddToGroceriesCommand
   {
        get
        {
            if (_addToGroceriesCommand == null)
            {
                _addToGroceriesCommand = new RelayCommand<FoodItem>(OnAddToGroceries);                    
            }
            return _addToGroceriesCommand;
        }
    }

   public void OnAddToGroceries(FoodItem newItem)
   {

   }

XAML :

   <ItemsControl ItemsSource="{Binding MyItemList}">
      <ItemsControl.ItemsTemplate>
         <DataTemplate>
             <Button Content="Buy" 
                     Command="{Binding Path=DataContext.AddToGroceriesCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                     CommandParameter="{Binding}" />
         </DataTemplate> 
      </ItemsControl.ItemsTemplate>
   </ItemsControl>