在WPF中的ItemsControl中使用事件和命令

时间:2013-05-31 19:27:47

标签: .net wpf data-binding wpf-controls

假设我有一个标准的WPF ItemsControl绑定到ObservableCollection的“Dog”对象,如下所示:

<ItemsControl ItemsSource="{Binding Dogs}">
<ItemsControl.ItemTemplate>
    <DataTemplate>
         <StackPanel Orientation="Horizontal">
             <TextBlock Text="{Binding Name}"/>
             <TextBlock Text="{Binding Breed}"/>
             <TextBlock Text="{Binding Weight}"/>
         </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

我希望用户能够删除集合中的任何狗。在过去,我一直使用ListBox控件并将我的ViewModel绑定到SelectedItem属性。然后我创建一个带有事件的按钮,该事件从ObservableCollection中删除所选对象。

这个工作正常,但是我想把它排好,所以每一行都有自己的删除按钮。

<ItemsControl ItemsSource="{Binding Dogs}">
<ItemsControl.ItemTemplate>
    <DataTemplate>
         <StackPanel Orientation="Horizontal">
             <TextBlock Text="{Binding Name}"/>
             <TextBlock Text="{Binding Breed}"/>
             <TextBlock Text="{Binding Weight}"/>
             <Button Click="Click_EventHandler"/>
         </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

一个看起来像这样的事件:

private void ListBox_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
          //Delete this Dog Object from Observable Collection
    }

将一个按钮推入ItemTemplate并给它一个事件会导致WPF崩溃,并且将一个命令绑定到一个ItemTemplate中的按钮根本不会做任何事情,所以我以前的方法将不起作用。

我能想到这样做的唯一方法是将ToggleButton添加到ItemTemplate并绑定到View Model,然后在Setter中触发事件。不完全是一个优雅的解决方案。

任何人都对如何解决这个问题有更好的了解?

1 个答案:

答案 0 :(得分:1)

你可以像其他一样绑定命令,但首先你需要实现ICommand接口,如下所示:

public class RelayCommand: ICommand
{
  private Action<object> _execute;
  private Predicate<object> _canExecute;

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

  public RelayCommand(Action<object> execute) : this(execute, null) { }

  public event EventHandler CanExecuteChanged;

  public bool CanExecute(object parameter)
  {
      return _canExecute != null ? _canExecute(parameter) : true;
  }

  public void Execute(object parameter)
  {
      if (CanExecute(parameter) && _execute != null) _execute(parameter);
  }
}

然后你的Dog类需要公开例如ICommand DeleteCmd属性:

class Dog
{
   ...
   private RelayCommand _deleteCmd;

   private void DoDelete(object parameter)
   {
      //put delete action here
   }

   public ICommand DeleteCmd
   {
      get
      {
         if (_deleteCmd == null) _deleteCmd = new RelayCommand(o => DoDelete(o));
         return _deleteCmd;
      }
   }
}

然后你像这样绑定它:

<StackPanel Orientation="Horizontal">
    <TextBlock Text="{Binding Name}"/>
    <TextBlock Text="{Binding Breed}"/>
    <TextBlock Text="{Binding Weight}"/>
    <Button Command="{Binding DeleteCmd}"/>
</StackPanel>