WPF ListView – 检测何时被选中的项目被点击

我正在使用WPF ListView控件显示数据绑定项目的列表。

<ListView ItemsSource={Binding MyItems}> <ListView.View> <GridView> <!-- declare a GridViewColumn for each property --> </GridView> </ListView.View> </ListView> 

我试图获得类似于ListView.SelectionChanged事件的行为,只有我想也检测当前选定的项目是否被点击。 如果再次单击相同的项目(显然) SelectionChanged事件不会触发。

什么是最好的(最干净的)方法来处理这个?

使用ListView.ItemContainerStyle属性为ListViewItems提供一个处理PreviewMouseLeftButtonDown事件的EventSetter。 然后,在处理程序中,检查是否选中了被单击的项目。

XAML:

 <ListView ItemsSource={Binding MyItems}> <ListView.View> <GridView> <!-- declare a GridViewColumn for each property --> </GridView> </ListView.View> <ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" /> </Style> </ListView.ItemContainerStyle> </ListView> 

代码隐藏:

 private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var item = sender as ListViewItem; if (item != null && item.IsSelected) { //Do your stuff } } 

您可以处理ListView的PreviewMouseLeftButtonUp事件。 不处理PreviewMouseLeftButtonDown事件的原因是,当你处理事件的时候,ListView的SelectedItem可能仍然是null。

XAML:

 <ListView ... PreviewMouseLeftButtonUp="listView_Click"> ... 

代码后面:

 private void listView_Click(object sender, RoutedEventArgs e) { var item = (sender as ListView).SelectedItem; if (item != null) { ... } } 

这些都是很好的build议,但如果我是你,我会在你的视图模型中做到这一点。 在您的视图模型中,您可以创build一个中继命令,然后将其绑定到项目模板中的单击事件。 要确定是否select了相同的项目,可以在视图模型中存储对所选项目的引用。 我喜欢使用MVVM Light来处理绑定。 这使得您的项目将来更容易修改,并允许您在Blend中设置绑定。

当所有的事情都说完之后,你的XAML将看起来像是谢尔盖所build议的。 我会避免在你的视图中使用代码。 我将避免在这个答案中写代码,因为这里有很多例子。

下面是一个: 如何在MVVM Light框架中使用RelayCommand

如果您需要示例,请评论,我将添加一个。

〜干杯

我说我不打算做一个例子,但我是。 干得好。

1)在您的项目中,仅添加MVVM Light Libraries。

2)为您的视图创build一个类。 一般来说,每个视图都有一个视图模型(view:MainWindow.xaml && viewModel:MainWindowViewModel.cs)

3)这是非常非常非常基本的视图模型的代码:

所有包含的命名空间(如果它们出现在这里,我假设你已经添加了对它们的引用。MVVM Light在Nuget中)

 using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.CommandWpf; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; 

现在添加一个基本的公共类:

 /// <summary> /// Very basic model for example /// </summary> public class BasicModel { public string Id { get; set; } public string Text { get; set; } /// <summary> /// Constructor /// </summary> /// <param name="text"></param> public BasicModel(string text) { this.Id = Guid.NewGuid().ToString(); this.Text = text; } } 

现在创build你的viewmodel:

 public class MainWindowViewModel : ViewModelBase { public MainWindowViewModel() { ModelsCollection = new ObservableCollection<BasicModel>(new List<BasicModel>() { new BasicModel("Model one") , new BasicModel("Model two") , new BasicModel("Model three") }); } private BasicModel _selectedBasicModel; /// <summary> /// Stores the selected mode. /// </summary> /// <remarks>This is just an example, may be different.</remarks> public BasicModel SelectedBasicModel { get { return _selectedBasicModel; } set { Set(() => SelectedBasicModel, ref _selectedBasicModel, value); } } private ObservableCollection<BasicModel> _modelsCollection; /// <summary> /// List to bind to /// </summary> public ObservableCollection<BasicModel> ModelsCollection { get { return _modelsCollection; } set { Set(() => ModelsCollection, ref _modelsCollection, value); } } } 

在你的viewmodel中,添加一个relaycommand。 请注意,我做了这个asynchronous,并通过了一个参数。

  private RelayCommand<string> _selectItemRelayCommand; /// <summary> /// Relay command associated with the selection of an item in the observablecollection /// </summary> public RelayCommand<string> SelectItemRelayCommand { get { if (_selectItemRelayCommand == null) { _selectItemRelayCommand = new RelayCommand<string>(async (id) => { await selectItem(id); }); } return _selectItemRelayCommand; } set { _selectItemRelayCommand = value; } } /// <summary> /// I went with async in case you sub is a long task, and you don't want to lock you UI /// </summary> /// <returns></returns> private async Task<int> selectItem(string id) { this.SelectedBasicModel = ModelsCollection.FirstOrDefault(x => x.Id == id); Console.WriteLine(String.Concat("You just clicked:", SelectedBasicModel.Text)); //Do async work return await Task.FromResult(1); } 

在为你查看的代码中,为你的viewmodel创build一个属性,并将你的视图的datacontext设置为viewmodel(请注意,还有其他方法可以做到这一点,但我正在试图做一个简单的例子)。

 public partial class MainWindow : Window { public MainWindowViewModel MyViewModel { get; set; } public MainWindow() { InitializeComponent(); MyViewModel = new MainWindowViewModel(); this.DataContext = MyViewModel; } } 

在您的XAML中,您需要在代码的顶部添加一些名称空间

 <Window x:Class="Basic_Binding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:Custom="clr-namespace:GalaSoft.MvvmLight;assembly=GalaSoft.MvvmLight" Title="MainWindow" Height="350" Width="525"> 

我添加了“我”和“自定义”。

这里是ListView:

 <ListView Grid.Row="0" Grid.Column="0" HorizontalContentAlignment="Stretch" ItemsSource="{Binding ModelsCollection}" ItemTemplate="{DynamicResource BasicModelDataTemplate}"> </ListView> 

这里是ListView的ItemTemplate:

 <DataTemplate x:Key="BasicModelDataTemplate"> <Grid> <TextBlock Text="{Binding Text}"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseLeftButtonUp"> <i:InvokeCommandAction Command="{Binding DataContext.SelectItemRelayCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding Id}"> </i:InvokeCommandAction> </i:EventTrigger> </i:Interaction.Triggers> </TextBlock> </Grid> </DataTemplate> 

运行你的应用程序,并签出输出窗口。 您可以使用转换器来处理所选项目的样式。

这可能看起来很复杂,但是当你需要将视图与ViewModel分离时(例如,为多个平台开发一个ViewModel),它会让生活变得更容易。另外,它使Blend 10x中的工作更加容易。 一旦你开发你的ViewModel,你可以把它交给devise师,让它看起来很艺术:)。 MVVM Light添加了一些function,使Blend识别您的ViewModel。 大部分情况下,你可以在ViewModel中做所有你想做的事情来影响视图。

如果有人读这个,我希望你觉得这个有帮助。 如果您有问题,请告诉我。 在这个例子中,我使用了MVVM Light,但是您可以在没有MVVM Light的情况下执行此操作。

〜干杯

你可以像这样处理点击列表视图项目:

 <ListView.ItemTemplate> <DataTemplate> <Button BorderBrush="Transparent" Background="Transparent" Focusable="False"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding DataContext.MyCommand, ElementName=ListViewName}" CommandParameter="{Binding}"/> </i:EventTrigger> </i:Interaction.Triggers> <Button.Template> <ControlTemplate> <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> ...