1. 程式人生 > 其它 >在WPF中的ItemsControl中使用事件和命令(Using events and Commands within ItemsControl in WPF)

在WPF中的ItemsControl中使用事件和命令(Using events and Commands within ItemsControl in WPF)

Say I have a standard WPF ItemsControl bound to an ObservableCollection of "Dog" objects like so:

<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>

I want the user to be able delete any of the dogs in the collection. In the past I've been doing this with a ListBox control and binding my ViewModel to the SelectedItem property. I then create a button with an event that removes the selected object from the ObservableCollection.

This works OK but I'd like to lay it out so each row can have its own delete button.

<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>

And an event that looks like this:

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

Shoving a button into the ItemTemplate and giving it an event crashes WPF, and binding a command to a button within an ItemTemplate doesn't do anything at all so my former method will not work.

The only way I can think of doing this is adding a ToggleButton to the ItemTemplate and binding to the View Model, and then firing an event in the Setter. Not exactly an elegant solution.

Anyone have any better idea on how to go about this?

解決方案

You can bind commands like everything else, but first you need your implementation of ICommand interface, something like this:

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);
  }
}

and then your Dog class needs to expose for example ICommand DeleteCmd property:

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;
      }
   }
}

and then you bind it like this:

<StackPanel Orientation="Horizontal">
    <TextBlock Text="{Binding Name}"/>
    <TextBlock Text="{Binding Breed}"/>
    <TextBlock Text="{Binding Weight}"/>
    <Button Command="{Binding DeleteCmd}"/>
</StackPanel>
說我有一個標準的WPF ItemsControl繫結到一個ObservableCollection的"狗"物件像這樣:

 < ItemsControl ItemsSource ="{繫結狗}"> 
< ItemsControl.ItemTemplate>
< DataTemplate>
< StackPanel Orientation ="Horizo​​ntal">
< 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 ="Horizo​​ntal">
< TextBlock Text ="{Binding Name}"/>
< TextBlock Text ="{Binding Breed}"/>
< TextBlock Text ="{Binding Weight}"/>
< Button Click ="Click_EventHandler"/>
< / StackPanel>
< / DataTemplate>
< /ItemsControl.ItemTemplate>
< / ItemsControl>


另外一個類似這樣的事件:



< pre> private void ListBox_PreviewMouseDown(object sender,System.Windows.Input.MouseButtonEventArgs e)
{
//從可觀察集合中刪除此Dog物件
}


將一個按鈕放入ItemTemplate並給它一個事件崩潰WPF,並將命令綁​​定到一個按鈕在ItemTemplate中沒有任何東西,所以我以前的方法將無法正常工作。



我可以想到這樣做的唯一方法是將一個ToggleButton新增到ItemTemplate,繫結到檢視模型,然後在Setter中觸發一個事件。不完全是一個優雅的解決方案。



任何人都有更好的想法如何去做這個?


解決方案

您可以像其他一樣繫結命令,但首先需要實現 ICommand 介面,如下所示:



  public class RelayCommand:ICommand 
{
private Action< object> _執行;
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(引數):true;
}

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


然後您的 / code>類需要公開,例如 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 ="Horizo​​ntal"> 
< TextBlock Text ="{Binding Name}"/>
< TextBlock Text ="{Binding Breed}"/>
< TextBlock Text ="{Binding Weight}"/>
< Button Command ="{Binding DeleteCmd}"/>
< / StackPanel>