在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>
< 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>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界