【问题标题】:WPF ListView: Attaching a double-click (on an item) eventWPF ListView:附加双击(在项目上)事件
【发布时间】:2010-10-18 05:04:13
【问题描述】:

我有以下ListView

<ListView Name="TrackListView">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Title" Width="100" 
                            HeaderTemplate="{StaticResource BlueHeader}" 
                            DisplayMemberBinding="{Binding Name}"/>

            <GridViewColumn Header="Artist" Width="100"  
                            HeaderTemplate="{StaticResource BlueHeader}"  
                            DisplayMemberBinding="{Binding Album.Artist.Name}" />
        </GridView>
    </ListView.View>
</ListView>

如何将事件附加到每个绑定的项目,双击该项目时会触发?

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    从这里找到解决方案:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3d0eaa54-09a9-4c51-8677-8e90577e7bac/


    XAML:

    <UserControl.Resources>
        <Style x:Key="itemstyle" TargetType="{x:Type ListViewItem}">
            <EventSetter Event="MouseDoubleClick" Handler="HandleDoubleClick" />
        </Style>
    </UserControl.Resources>
    
    <ListView Name="TrackListView" ItemContainerStyle="{StaticResource itemstyle}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Title" Width="100" HeaderTemplate="{StaticResource BlueHeader}" DisplayMemberBinding="{Binding Name}"/>
                <GridViewColumn Header="Artist" Width="100" HeaderTemplate="{StaticResource BlueHeader}" DisplayMemberBinding="{Binding Album.Artist.Name}" />
            </GridView>
        </ListView.View>
    </ListView>
    

    C#:

    protected void HandleDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var track = ((ListViewItem) sender).Content as Track; //Casting back to the binded Track
    }
    

    【讨论】:

    • 如果不需要复用样式,可以直接放到部分,去掉x:Key。
    • 这对我也有用。谢谢!顺便说一句,您可能希望通过设置来停止处理程序中 doubleClick 事件的冒泡: e.Handled = true;
    • 我对此有疑问。也就是说,我在窗口中使用 x:Key-less 样式来设置所有 UI 元素的样式,包括在该窗口上的自定义控件中使用的 ListView。将此事件处理程序放在自定义控件的 xaml 中会禁用在窗口中应用的样式。
    • 只是出于好奇,还有其他不违反 MVVM 的方法吗?
    • 作为警告:如果其处理程序的目标寿命比ListViewItem 长,使用EventSetter 可能会导致内存泄漏。过去几天我调试了一个严重的内存泄漏(一次 20mb),却发现ListViewItems 及其相关的内存正在通过EventSetter 泄漏。
    【解决方案2】:

    没有内存泄漏(无需取消订阅每个项目),工作正常:

    XAML:

    <ListView MouseDoubleClick="ListView_MouseDoubleClick" ItemsSource="{Binding TrackCollection}" />
    

    C#:

        void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var item = ((FrameworkElement) e.OriginalSource).DataContext as Track;
            if (item != null)
            {
                MessageBox.Show("Item's Double Click handled!");
            }
        }
    

    【讨论】:

    • 非常棒,再也不用担心内存泄漏了,坦率地说,它只是干净多了。
    • 如果您的列表包含复杂对象,这还不够。您需要使用可视化树助手来查找父 ListViewItem 并从那里获取数据上下文
    • 干净简单。谢谢。
    • 非常好,乐于助人。就我而言,我有一个额外的选择按钮来执行选择操作。所以我使用双击如下: 'MouseDoubleClick="SelectBtn_Click"' 'private void SelectBtn_Click(object sender, RoutedEventArgs e) { }'
    • 这就是为什么你总是滚动过去接受的答案。以防万一……
    【解决方案3】:

    我的解决方案是基于@epox_sub's answer,您应该查看在 XAML 中放置事件处理程序的位置。代码隐藏对我不起作用,因为我的 ListViewItems 是复杂对象。 @sipwiz's answer 是一个很好的提示,告诉你去哪里看......

    void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var item = ListView.SelectedItem as Track;
        if (item != null)
        {
          MessageBox.Show(item + " Double Click handled!");
        }
    }
    

    这样做的好处是您获得了SelectedItem 的DataContext 绑定(在这种情况下为Track)。 Selected Item 之所以有效,是因为第一次双击时会选中它。

    【讨论】:

    • 这很好用,但不幸的是,当用户双击列表视图中的空白区域时也会触发双击事件,并且由于用户单击空白区域时未清除 SelectedItem,因此该事件在先前选择的项目上执行。
    【解决方案4】:

    我使用的替代方法是事件到命令,

    <ListView ItemsSource="{Binding SelectedTrack}" SelectedItem="{Binding SelectedTrack}" >
        <i:Interaction.Triggers>
             <i:EventTrigger EventName="MouseDoubleClick">
                  <i:InvokeCommandAction Command="{Binding SelectTrackCommand}"/>
             </i:EventTrigger>
        </i:Interaction.Triggers>
        ...........
        ...........
    </ListView>
    

    【讨论】:

      【解决方案5】:

      对于那些对主要维护 MVVM 模式感兴趣的人,我使用了Andreas Grech's answer 来解决问题。

      基本流程:

      用户双击项目 -> 后面代码中的事件处理程序 -> ICommand in 查看模型

      ProjectView.xaml:

      <UserControl.Resources>
          <Style TargetType="ListViewItem" x:Key="listViewDoubleClick">
              <EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick"/>
          </Style>
      </UserControl.Resources>
      
      ...
      
      <ListView ItemsSource="{Binding Projects}" 
                ItemContainerStyle="{StaticResource listViewDoubleClick}"/>
      

      ProjectView.xaml.cs:

      public partial class ProjectView : UserControl
      {
          public ProjectView()
          {
              InitializeComponent();
          }
      
          private void ListViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
          {
              ((ProjectViewModel)DataContext)
                  .ProjectClick.Execute(((ListViewItem)sender).Content);
          }
      }
      

      ProjectViewModel.cs:

      public class ProjectViewModel
      {
          public ObservableCollection<Project> Projects { get; set; } = 
                     new ObservableCollection<Project>();
      
          public ProjectViewModel()
          {
              //Add items to Projects
          }
      
          public ICommand ProjectClick
          {
              get { return new DelegateCommand(new Action<object>(OpenProjectInfo)); }
          }
      
          private void OpenProjectInfo(object _project)
          {
              ProjectDetailView project = new ProjectDetailView((Project)_project);
              project.ShowDialog();
          }
      }
      

      可以在here找到DelegateCommand.cs。

      在我的例子中,我有一个填充ListViewProject 对象集合。这些对象包含的属性比列表中显示的要多,我打开一个 ProjectDetailView(一个 WPF Window)来显示它们。

      事件处理程序的sender 对象是选定的ListViewItem。随后,我想要访问的Project 包含在Content 属性中。

      【讨论】:

        【解决方案6】:

        在您的示例中,您是否尝试捕获 ListView 中的项目何时被选中或列标题被单击?如果是前者,您将添加一个 SelectionChanged 处理程序。

        <ListView Name="TrackListView" SelectionChanged="MySelectionChanged">
        

        如果是后者,您必须在 GridViewColumn 项目上使用 MouseLeftButtonUp 或 MouseLeftButtonDown 事件的某种组合来检测双击并采取适当的操作。或者,您可以处理 GridView 上的事件并从那里计算出鼠标下方的列标题。

        【讨论】:

        • 我想要一个关于有界项目的事件,而不是标题
        • 这对我来说是新的。感谢您提出您的答案(我会删除我的 no DoubleClick 事件声明)。
        【解决方案7】:

        epox_spb's answer 的基础上,我添加了一个检查以避免双击 GridViewColumn 标题时出现错误。

        void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var dataContext = ((FrameworkElement)e.OriginalSource).DataContext;
            if (dataContext is Track)
            {
                MessageBox.Show("Item's Double Click handled!");
            }
        }
        

        【讨论】:

        • 非常酷 - 与 PowerShell 一起使用 - $myListView.Add_MouseDoubleClick({ Param($sender, $ev); $e = [System.Windows.Input.MouseButtonEventArgs]$ev; $itemData = ([System.Windows.FrameworkElement]$e.OriginalSource).DataContext }); if ($item -ne $null) { Write-Host $itemData; } }) --- 不需要强制转换,但有助于 ISE 完成
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-21
        • 2010-11-05
        • 1970-01-01
        • 1970-01-01
        • 2011-03-23
        • 2020-10-23
        • 2019-03-17
        相关资源
        最近更新 更多