WPF EventSetter Handler Command
最近做一个工具,突然发现ListBox和ListView等列表控件的MouseDoubleClick事件有时候是获取不到当前双击的行对象数据的,比如这样写:
<ListBox Grid.Row="1" ItemsSource="{Binding DataList}" MouseDoubleClick="ListBox_MouseDoubleClick" SelectedItem="{Binding CurrentSelectItem}" Background="AliceBlue"> <ListBox.ItemTemplate> <DataTemplate> <DockPanel Height="50" Background="DarkGray" Width="300"> <TextBox Text="{Binding Name}" Height="30" Width="200" Background="DimGray"></TextBox> </DockPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
private void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) { ListBox listBox = sender as ListBox; if (listBox == null || listBox.SelectedItem == null) { MessageBox.Show("ListBox1双击对象为空..."); } else { var model = listBox.SelectedItem as ListBoxModel; MessageBox.Show("当前对象为" + model.Name + " " + model.Age); } }
双击行就会出现双击的对象为空。
上一篇文章中已经说明怎么解决这个问题:
http://www.cnblogs.com/ligl/p/5629802.html
使用Style中的EventSetter Handler这里就不在更多介绍。
但是今天想要解决的问题是怎么把EventSetter Handler使用Command绑定的方式把Handler事件进行解耦
要使用第三方类库CommandBehavior(AttachedCommandBehavior acb)进行解耦
代码如下:
引用 xmlns:localCommand="clr-namespace:AttachedCommandBehavior"
<Style x:Key="listBox2Item" TargetType="ListBoxItem">
<Style.Setters>
<Setter Property="localCommand:CommandBehavior.Event" Value="MouseDoubleClick"></Setter>
<Setter Property="localCommand:CommandBehavior.Command" Value="{Binding DataContext.DoubleCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:WinTest}}}"></Setter>
<Setter Property="localCommand:CommandBehavior.CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}}"></Setter>
</Style.Setters>
</Style>
ViewModel代码如下
public class ViewModel : INotifyPropertyChanged { public ViewModel() { for (int i = 0; i < 5; i++) { DataList.Add(new ListBoxModel() { Name = "张三" + i.ToString(), Age = 1000 + i }); DataList2.Add(new ListBoxModel() { Name = "李四" + i.ToString(), Age = 100 + i }); } doubleCommand = new SimpleCommand(obj => { ListBoxItem listBoxItem = obj as ListBoxItem; if (listBoxItem != null) { ListBoxModel model = listBoxItem.Content as ListBoxModel; if (model != null) { CurrentSelectItem2 = model; MessageBox.Show("Command Banding" + model.Name + " " + model.Age); } } //wpftest.ViewModel MessageBox.Show("Cmd..."); }, o => true); } public SimpleCommand DoubleCommand { get { return doubleCommand; } set { doubleCommand = value; //OnPropertyChanged(new PropertyChangedEventArgs("DoubleCommand")); } } private ObservableCollection<ListBoxModel> dataList = new ObservableCollection<ListBoxModel>(); private ObservableCollection<ListBoxModel> _dataList2 = new ObservableCollection<ListBoxModel>(); private ListBoxModel _CurrentSelectItem; private ListBoxModel _CurrentSelectItem2; private SimpleCommand doubleCommand; public ObservableCollection<ListBoxModel> DataList { get { return dataList; } set { dataList = value; } } /// <summary> /// 当前双击的对象 /// </summary> public ListBoxModel CurrentSelectItem { get { return _CurrentSelectItem; } set { _CurrentSelectItem = value; OnPropertyChanged(new PropertyChangedEventArgs("CurrentSelectItem")); } } /// <summary> /// ListBox2双击的对象 /// </summary> public ListBoxModel CurrentSelectItem2 { get { return _CurrentSelectItem2; } set { _CurrentSelectItem2 = value; OnPropertyChanged(new PropertyChangedEventArgs("CurrentSelectItem2")); } } public ObservableCollection<ListBoxModel> DataList2 { get { return _dataList2; } set { _dataList2 = value; } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) { PropertyChanged(this, e); } } }
完整Xaml和CS代码如下:
<Window x:Class="WpfTest.WinTest" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfTest" xmlns:localCommand="clr-namespace:AttachedCommandBehavior" mc:Ignorable="d" Title="WinTest" Height="800" Width="800"> <Window.Resources> <Style TargetType="TextBlock"> <Style.Setters> <Setter Property="FontSize" Value="20"></Setter> </Style.Setters> </Style> <Style TargetType="Button"> <Style.Setters> <Setter Property="localCommand:CommandBehavior.Event" Value="MouseDoubleClick"></Setter> <Setter Property="localCommand:CommandBehavior.Command" Value="{Binding DoubleCommand}"></Setter> <Setter Property="localCommand:CommandBehavior.CommandParameter" Value="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:WinTest}}}"></Setter> </Style.Setters> </Style> <Style x:Key="listBox2Item" TargetType="ListBoxItem"> <Style.Setters> <Setter Property="localCommand:CommandBehavior.Event" Value="MouseDoubleClick"></Setter> <Setter Property="localCommand:CommandBehavior.Command" Value="{Binding DataContext.DoubleCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:WinTest}}}"></Setter> <Setter Property="localCommand:CommandBehavior.CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}}"></Setter> </Style.Setters> </Style> <!--<Style x:Key="listBox2Item" TargetType="ListBoxItem"> <Style.Setters> <EventSetter Event="MouseDoubleClick" Handler="ListBox2_MouseDoubleClick"></EventSetter> </Style.Setters> </Style>--> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <StackPanel Margin="0 0 20 0"> <TextBlock Text="{Binding CurrentSelectItem.Name}"></TextBlock> <TextBlock Text="{Binding CurrentSelectItem.Age}"></TextBlock> </StackPanel> <StackPanel> <TextBlock Text="{Binding CurrentSelectItem2.Name}"> </TextBlock> <TextBlock Text="{Binding CurrentSelectItem2.Age}"></TextBlock> </StackPanel> <Button Content="DoubleClick" ></Button> </StackPanel> <ListBox Grid.Row="1" ItemsSource="{Binding DataList}" MouseDoubleClick="ListBox_MouseDoubleClick" SelectedItem="{Binding CurrentSelectItem}" Background="AliceBlue"> <ListBox.ItemTemplate> <DataTemplate> <DockPanel Height="50" Background="DarkGray" Width="300"> <TextBox Text="{Binding Name}" Height="30" Width="200" Background="DimGray"></TextBox> </DockPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <ListBox Grid.Row="2" ItemsSource="{Binding DataList2}" SelectedItem="{Binding CurrentSelectItem2}" ItemContainerStyle="{StaticResource listBox2Item}" Background="Silver"> <ListBox.ItemTemplate> <DataTemplate> <DockPanel Height="50" Background="DarkOrange" Width="300"> <TextBox Text="{Binding Name}" Height="30" Width="200" Background="DarkCyan"></TextBox> </DockPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
using AttachedCommandBehavior; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace WpfTest { /// <summary> /// WinTest.xaml 的交互逻辑 /// </summary> public partial class WinTest : Window { ViewModel VModel = new ViewModel(); public WinTest() { InitializeComponent(); this.DataContext = VModel; } private void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) { ListBox listBox = sender as ListBox; if (listBox == null || listBox.SelectedItem == null) { MessageBox.Show("ListBox1双击对象为空..."); } else { var model = listBox.SelectedItem as ListBoxModel; MessageBox.Show("当前对象为" + model.Name + " " + model.Age); } } private void ListBox2_MouseDoubleClick(object sender, MouseButtonEventArgs e) { ListBoxItem listBoxItem = sender as ListBoxItem; if (listBoxItem == null) { MessageBox.Show("ListBox2双击对象为空..."); } else { ListBoxModel model = listBoxItem.Content as ListBoxModel; if (model != null) { VModel.CurrentSelectItem2 = listBoxItem.Content as ListBoxModel; MessageBox.Show(model.Name + " " + model.Age); } } } } public class ViewModel : INotifyPropertyChanged { public ViewModel() { for (int i = 0; i < 5; i++) { DataList.Add(new ListBoxModel() { Name = "张三" + i.ToString(), Age = 1000 + i }); DataList2.Add(new ListBoxModel() { Name = "李四" + i.ToString(), Age = 100 + i }); } doubleCommand = new SimpleCommand(obj => { ListBoxItem listBoxItem = obj as ListBoxItem; if (listBoxItem != null) { ListBoxModel model = listBoxItem.Content as ListBoxModel; if (model != null) { CurrentSelectItem2 = model; MessageBox.Show("Command Banding" + model.Name + " " + model.Age); } } //wpftest.ViewModel MessageBox.Show("Cmd..."); }, o => true); } public SimpleCommand DoubleCommand { get { return doubleCommand; } set { doubleCommand = value; //OnPropertyChanged(new PropertyChangedEventArgs("DoubleCommand")); } } private ObservableCollection<ListBoxModel> dataList = new ObservableCollection<ListBoxModel>(); private ObservableCollection<ListBoxModel> _dataList2 = new ObservableCollection<ListBoxModel>(); private ListBoxModel _CurrentSelectItem; private ListBoxModel _CurrentSelectItem2; private SimpleCommand doubleCommand; public ObservableCollection<ListBoxModel> DataList { get { return dataList; } set { dataList = value; } } /// <summary> /// 当前双击的对象 /// </summary> public ListBoxModel CurrentSelectItem { get { return _CurrentSelectItem; } set { _CurrentSelectItem = value; OnPropertyChanged(new PropertyChangedEventArgs("CurrentSelectItem")); } } /// <summary> /// ListBox2双击的对象 /// </summary> public ListBoxModel CurrentSelectItem2 { get { return _CurrentSelectItem2; } set { _CurrentSelectItem2 = value; OnPropertyChanged(new PropertyChangedEventArgs("CurrentSelectItem2")); } } public ObservableCollection<ListBoxModel> DataList2 { get { return _dataList2; } set { _dataList2 = value; } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) { PropertyChanged(this, e); } } } public class ListBoxModel : INotifyPropertyChanged { /// <summary> /// 姓名 /// </summary> private string _Name; /// <summary> /// 年龄 /// </summary> private int _Age; public string Name { get { return _Name; } set { _Name = value; OnPropertyChanged(new PropertyChangedEventArgs("Name")); } } public int Age { get { return _Age; } set { _Age = value; OnPropertyChanged(new PropertyChangedEventArgs("Age")); } } public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) { PropertyChanged(this, e); } } public event PropertyChangedEventHandler PropertyChanged; } }
<Setter Property="localCommand:CommandBehavior.Command" Value="{Binding DataContext.DoubleCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:WinTest}}}"></Setter>
关于这个Command的Value绑定要使用FindAncestor进行查找才能解决,不然是绑定不到ViewModel中的DoubleCommand
发个图看看:
关于CommandBehavior代码可以在
http://download.csdn.net/download/doncle000/7029327 下载使用
国外博客http://marlongrech.wordpress.com/2008/12/04/attachedcommandbehavior-aka-acb/
对于SimpleCommad.cs的源文件我增加了两个参数的构造函数:
public SimpleCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); CanExecuteDelegate = canExecute; ExecuteDelegate = execute; }