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>
< ItemsControl ItemsSource ="{绑定狗}">
< 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>
另外一个类似这样的事件:
< 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 ="Horizontal">
< TextBlock Text ="{Binding Name}"/>
< TextBlock Text ="{Binding Breed}"/>
< TextBlock Text ="{Binding Weight}"/>
< Button Command ="{Binding DeleteCmd}"/>
< / StackPanel>