WPF ListView-在单击所选项目时检测

WPF ListView-在单击所选项目时检测,wpf,listview,Wpf,Listview,我使用的是WPF ListView控件,它显示数据绑定项的列表 <ListView ItemsSource={Binding MyItems}> <ListView.View> <GridView> <!-- declare a GridViewColumn for each property --> </GridView> </ListView.View&g

我使用的是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事件。然后,在处理程序中,检查是否选中了单击的项目

XAML:


您可以处理ListView的PreviewMouseLeftButtonUp事件。 不处理PreviewMouseLeftButtonDown事件的原因是,在处理该事件时,ListView的SelectedItem可能仍然为空

XAML:

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

您可以按如下方式处理单击列表视图项:

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

...

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

当所有这些都说了又做了之后,您的XAML将像Sergey建议的那样。在你看来,我会避免使用隐藏的代码。我将避免在这个答案中编写代码,因为有大量的例子

这里有一个:

如果你需要一个例子,请评论,我会添加一个

~z~干杯

我说过我不会做一个例子,但我会的。给你

1) 在项目中,仅添加MVVM灯光库

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

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

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

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

/// <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;
    }
}
在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 
        Grid.Row="0" 
        Grid.Column="0" 
        HorizontalContentAlignment="Stretch"
        ItemsSource="{Binding ModelsCollection}"
        ItemTemplate="{DynamicResource BasicModelDataTemplate}">
    </ListView>
<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>

以下是ListView的ItemTemplate:

<ListView 
        Grid.Row="0" 
        Grid.Column="0" 
        HorizontalContentAlignment="Stretch"
        ItemsSource="{Binding ModelsCollection}"
        ItemTemplate="{DynamicResource BasicModelDataTemplate}">
    </ListView>
<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后,可以将其交给设计师,让其看起来非常艺术:)。MVVM灯光添加了一些功能,使Blend能够识别您的ViewModel。在大多数情况下,您可以在ViewModel中执行任何想要影响视图的操作

如果有人读到这篇文章,我希望你会觉得这篇文章很有帮助。如果你有问题,请告诉我。在本例中,我使用了MVVM灯光,但您可以在没有MVVM灯光的情况下完成此操作


~Cheers

我还建议在单击某个项目后取消选择该项目,并使用MouseDoubleClick事件

private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    try {
        //Do your stuff here
        listBox.SelectedItem = null;
        listBox.SelectedIndex = -1;
    } catch (Exception ex) {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
}

我无法得到我想要的答案(见Farrukh的评论)

我提出了一个稍有不同的解决方案,这也让人感觉更自然,因为它在鼠标按钮按下时选择项目,然后当鼠标按钮松开时,您可以对其做出反应:

XAML:

这对我有用

单击一行将触发代码隐藏

XAML:


实际上,您可以直接在
列表视图上设置处理程序,不需要EventSetter.item.IsSelected在第一次单击时无效,因此我必须双击。如果(item!=null){}工作正常。@Gábor这样设置处理程序在选择item时不会触发。@FarrukhWaheed我添加了另一个解决您的问题的答案。您的答案是所有答案中最好的,但我将扩展您绑定到的视图模型,因为前面的答案提到了使用隐藏代码+1从技术上讲,这是一个正确的答案,但我不会这么做。我建议不要使用代码隐藏。我没有否决投票,但我建议在您的视图模型中设置此选项。这不是正确的答案,因为用户可以单击ListView中的空白区域,如果当前有选定的项目,则将触发该操作,就像用户单击选定的项目一样。@Rogala代码隐藏本身没有问题。您的业务逻辑不应依赖于您的应用程序代码,您的应用程序代码也不应依赖于您的UI代码。如果您的业务逻辑与任何应用程序代码一起工作,并且您的应用程序代码与任何UI一起工作,那么您已经实现了MVVM。在不考虑上下文的情况下应用绝对的、人为的限制必然会限制代码质量。如果没有C#代码,某些UI行为是不可能的,调用ViewModel.SuperNeatCommand.Execute(param)与绑定到它完全相同。附加属性在这里工作得很好,但它并不总是最好的,因为YAGNI。@Daniel实际上,代码背后固有的错误是倾向于向其添加业务逻辑,而不是编写业务逻辑
public partial class MainWindow : Window
{
    public MainWindowViewModel MyViewModel { get; set; }
    public MainWindow()
    {
        InitializeComponent();

        MyViewModel = new MainWindowViewModel();
        this.DataContext = MyViewModel;
    }
}
<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 
        Grid.Row="0" 
        Grid.Column="0" 
        HorizontalContentAlignment="Stretch"
        ItemsSource="{Binding ModelsCollection}"
        ItemTemplate="{DynamicResource BasicModelDataTemplate}">
    </ListView>
<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>
private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    try {
        //Do your stuff here
        listBox.SelectedItem = null;
        listBox.SelectedIndex = -1;
    } catch (Exception ex) {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
}
<ListView Name="MyListView" ItemsSource={Binding MyItems}>
<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />
        <EventSetter Event="PreviewMouseLeftButtonUp" Handler="ListViewItem_PreviewMouseLeftButtonUp" />
    </Style>
</ListView.ItemContainerStyle>
private void ListViewItem_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    MyListView.SelectedItems.Clear();

    ListViewItem item = sender as ListViewItem;
    if (item != null)
    {
        item.IsSelected = true;
        MyListView.SelectedItem = item;
    }
}

private void ListViewItem_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    ListViewItem item = sender as ListViewItem;
    if (item != null && item.IsSelected)
    {
        // do stuff
    }
}
<ListView x:Name="MyListView" MouseLeftButtonUp="MyListView_MouseLeftButtonUp">
    <GridView>
        <!-- Declare GridViewColumns. -->
    </GridView>
</ListView.View>
private void MyListView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    System.Windows.Controls.ListView list = (System.Windows.Controls.ListView)sender;
    MyClass selectedObject = (MyClass)list.SelectedItem;
    // Do stuff with the selectedObject.
}