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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10207888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:23:18  来源:igfitidea点击:

WPF ListView - detect when selected item is clicked

wpflistview

提问by scripni

I'm using a WPF ListView control which displays a list of databound items.

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

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

I'm trying to obtain a behavior similar to the ListView.SelectionChangedevent, only I want to also detect if the currently selected item is clicked. The SelectionChangedevent does not fire if the same item is clicked again (obviously).

我正在尝试获得类似于ListView.SelectionChanged事件的行为,只是我还想检测是否单击了当前选定的项目。SelectionChanged如果再次单击同一项目(显然),则不会触发该事件。

What would be the best (cleanest) way to approach this?

解决这个问题的最佳(最干净)方法是什么?

回答by ChimeraObscura

Use the ListView.ItemContainerStyle property to give your ListViewItems an EventSetter that will handle the PreviewMouseLeftButtonDown event. Then, in the handler, check to see if the item that was clicked is selected.

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

XAML:

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>

Code-behind:

代码隐藏:

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

回答by lznt

You can handle the ListView's PreviewMouseLeftButtonUp event. The reason not to handle the PreviewMouseLeftButtonDown event is that, by the time when you handle the event, the ListView's SelectedItem may still be null.

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

XAML:

XAML:

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

Code behind:

后面的代码:

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

回答by Rogala

These are all great suggestions, but if I were you, I would do this in your view model. Within your view model, you can create a relay command that you can then bind to the click event in your item template. To determine if the same item was selected, you can store a reference to your selected item in your view model. I like to use MVVM Light to handle the binding. This makes your project much easier to modify in the future, and allows you to set the binding in Blend.

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

When all is said and done, your XAML will look like what Sergey suggested. I would avoid using the code behind in your view. I'm going to avoid writing code in this answer, because there is a ton of examples out there.

当一切都完成后,您的 XAML 将看起来像 Sergey 建议的那样。我会避免在您看来使用后面的代码。我将避免在此答案中编写代码,因为那里有大量示例。

Here is one: How to use RelayCommand with the MVVM Light framework

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

If you require an example, please comment, and I will add one.

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

~Cheers

~干杯

I said I wasn't going to do an example, but I am. Here you go.

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

1) In your project, add MVVM Light Libraries Only.

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

2) Create a class for your view. Generally speaking, you have a view model for each view (view: MainWindow.xaml && viewModel: MainWindowViewModel.cs)

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

3) Here is the code for the very, very, very basic view model:

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

All included namespace (if they show up here, I am assuming you already added the reference to them. MVVM Light is in Nuget)

所有包含的命名空间(如果它们出现在这里,我假设您已经添加了对它们的引用。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;

Now add a basic public class:

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

/// <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;
    }
}

Now create your 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); }
    }        
}

In your viewmodel, add a relaycommand. Please note, I made this async and had it pass a parameter.

在您的视图模型中,添加一个中继命令。请注意,我做了这个异步并让它传递了一个参数。

    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);
    }

In the code behind for you view, create a property for you viewmodel and set the datacontext for your view to the viewmodel (please note, there are other ways to do this, but I am trying to make this a simple example.)

在后面的代码中,为您的视图模型创建一个属性并将视图的数据上下文设置为视图模型(请注意,还有其他方法可以做到这一点,但我试图将其作为一个简单的例子。)

public partial class MainWindow : Window
{
    public MainWindowViewModel MyViewModel { get; set; }
    public MainWindow()
    {
        InitializeComponent();

        MyViewModel = new MainWindowViewModel();
        this.DataContext = MyViewModel;
    }
}

In your XAML, you need to add some namespaces to the top of your code

在您的 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">

I added "i" and "Custom."

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

Here is the ListView:

这是列表视图:

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

Here is the ItemTemplate for the 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>

Run your application, and check out the output window. You can use a converter to handle the styling of the selected item.

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

This may seem really complicated, but it makes life a lot easier down the road when you need to separate your view from your ViewModel (e.g. develop a ViewModel for multiple platforms.) Additionally, it makes working in Blend 10x easier. Once you develop your ViewModel, you can hand it over to a designer who can make it look very artsy :). MVVM Light adds some functionality to make Blend recognize your ViewModel. For the most part, you can do just about everything you want to in the ViewModel to affect the view.

这看起来确实很复杂,但是当您需要将视图与 ViewModel 分开时(例如,为多个平台开发一个 ViewModel),它会让您的工作变得更加轻松。此外,它还使在 Blend 10x 中工作变得更加容易。一旦你开发了你的 ViewModel,你就可以把它交给一个设计师,他可以让它看起来非常有艺术感:)。MVVM Light 添加了一些功能来让 Blend 识别您的 ViewModel。在大多数情况下,您可以在 ViewModel 中做任何您想做的事情来影响视图。

If anyone reads this, I hope you find this helpful. If you have questions, please let me know. I used MVVM Light in this example, but you could do this without MVVM Light.

如果有人读到这篇文章,我希望你觉得这对你有帮助。如果您有任何疑问,请告诉我。我在这个例子中使用了 MVVM Light,但你可以在没有 MVVM Light 的情况下做到这一点。

~Cheers

~干杯

回答by Sergey Sukhinin

You can handle click on list view item like this:

您可以像这样处理单击列表视图项:

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

回答by Cesar Daniel

This worked for me.

这对我有用。

Single-clicking a row triggers the code-behind.

单击一行会触发代码隐藏。

XAML:

XAML:

<ListView x:Name="MyListView" MouseLeftButtonUp="MyListView_MouseLeftButtonUp">
    <GridView>
        <!-- Declare GridViewColumns. -->
    </GridView>
</ListView.View>

Code-behind:

代码隐藏:

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

回答by Ivan Silkin

I would also suggest deselecting an item after it has been clicked and use the MouseDoubleClick event

我还建议在单击后取消选择一个项目并使用 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);
    }
}

回答by Stacksatty

I couldn't get the accepted answer to work the way I wanted it to (see Farrukh's comment).

我无法按照我希望的方式获得公认的答案(请参阅 Farrukh 的评论)。

I came up with a slightly different solution which also feels more native because it selects the item on mouse button down and then you're able to react to it when the mouse button gets released:

我想出了一个稍微不同的解决方案,它也感觉更原生,因为它选择鼠标按钮上的项目,然后当鼠标按钮被释放时你可以对它做出反应:

XAML:

XAML:

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

Code behind:

后面的代码:

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
    }
}