代码之家  ›  专栏  ›  技术社区  ›  Andreas Grech

WPF:显示网格视图项的上下文菜单

  •  18
  • Andreas Grech  · 技术社区  · 15 年前

    我有以下内容 GridView :

    <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}" />
                <GridViewColumn Header="Album" Width="100" HeaderTemplate="{StaticResource BlueHeader}" DisplayMemberBinding="{Binding Album.Name}"/>
                <GridViewColumn Header="Length" Width="100" HeaderTemplate="{StaticResource BlueHeader}"/>
            </GridView>
         </ListView.View>
    </ListView>
    

    现在,我想显示一个上下文菜单,右键单击一个有边界的项,它允许我在代码隐藏中处理事件时检索所选的项。

    我能以什么可能的方式来完成这个任务?


    [更新]

    跟随 Dennis Roche 的代码,我现在有了:

        <ListView Name="TrackListView" ItemContainerStyle="{StaticResource itemstyle}">
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnListViewItem_PreviewMouseLeftButtonDown" />
                    <Setter Property="ContextMenu">
                        <Setter.Value>
                            <ContextMenu>
                                <MenuItem Header="Add to Playlist"></MenuItem>
                            </ContextMenu>
                         </Setter.Value>
                    </Setter>
                </Style>
            </ListView.ItemContainerStyle>
    
            <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}" />
                    <GridViewColumn Header="Album" Width="100" HeaderTemplate="{StaticResource BlueHeader}" DisplayMemberBinding="{Binding Album.Name}"/>
                    <GridViewColumn Header="Length" Width="100" HeaderTemplate="{StaticResource BlueHeader}"/>
                </GridView>
             </ListView.View>
        </ListView>
    

    但在跑步时,我收到了一个例外:

    无法添加类型的内容 '系统.windows.controls.contextmenu' 到“System.Object”类型的对象。 对象错误 '系统.windows.controls.contextmenu' 在标记文件中 'Musicrepo_importer;组件/controls/trackgridcontrol.xaml'。

    有什么问题?

    3 回复  |  直到 12 年前
        1
  •  19
  •   Dennis    12 年前

    是,使用上下文菜单添加ListView.ItemContainerStyle。

    <ListView>
      <ListView.Resources>
        <ContextMenu x:Key="ItemContextMenu">
          ...
        </ContextMenu>
      </ListView.Resources>
      <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
          <EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnListViewItem_PreviewMouseLeftButtonDown" />
          <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}"/>
        </Style>
      </ListView.ItemContainerStyle>
    </ListView>
    

    注意:您需要将ContextMenu作为资源引用,并且不能在本地定义它。

    这将启用整行的上下文菜单。:)

    也看到我处理 PreviewMouseLeftButtonDown 事件,以便我可以确保该项处于焦点状态(并且在查询ListView时是当前选定的项)。我发现在应用程序之间更改焦点时必须这样做,在您的情况下可能不是这样。

    更新的

    在代码隐藏文件中,您需要在可视化树上查找列表容器项,因为事件的原始源可以是项模板的元素(例如stackpanel)。

    void OnListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      if (e.Handled)
        return;
    
      ListViewItem item = MyVisualTreeHelper.FindParent<ListViewItem>((DependencyObject)e.OriginalSource);
      if (item == null)
        return;
    
      if (item.Focusable && !item.IsFocused)
        item.Focus();
    }
    

    这个 MyVisualTreeHelper 这就是使用我编写的包装器来快速浏览可视树。子集发布在下面。

    public static class MyVisualTreeHelper
    {
      static bool AlwaysTrue<T>(T obj) { return true; }
    
      /// <summary>
      /// Finds a parent of a given item on the visual tree. If the element is a ContentElement or FrameworkElement 
      /// it will use the logical tree to jump the gap.
      /// If not matching item can be found, a null reference is returned.
      /// </summary>
      /// <typeparam name="T">The type of the element to be found</typeparam>
      /// <param name="child">A direct or indirect child of the wanted item.</param>
      /// <returns>The first parent item that matches the submitted type parameter. If not matching item can be found, a null reference is returned.</returns>
      public static T FindParent<T>(DependencyObject child) where T : DependencyObject
      {
        return FindParent<T>(child, AlwaysTrue<T>);
      }
    
      public static T FindParent<T>(DependencyObject child, Predicate<T> predicate) where T : DependencyObject
      {
        DependencyObject parent = GetParent(child);
        if (parent == null)
          return null;
    
        // check if the parent matches the type and predicate we're looking for
        if ((parent is T) && (predicate((T)parent)))
          return parent as T;
        else
          return FindParent<T>(parent);
      }
    
      static DependencyObject GetParent(DependencyObject child)
      {
        DependencyObject parent = null;
        if (child is Visual || child is Visual3D)
          parent = VisualTreeHelper.GetParent(child);
    
        // if fails to find a parent via the visual tree, try to logical tree.
        return parent ?? LogicalTreeHelper.GetParent(child);
      }
    }
    

    我希望这些额外的信息有帮助。

    丹尼斯

        2
  •  9
  •   Omzig    12 年前

    丹尼斯

    喜欢这个例子,但是我没有找到任何需要你的可视化树助手…

       <ListView.Resources>
        <ContextMenu x:Key="ItemContextMenu">
            <MenuItem x:Name="menuItem_CopyUsername"
                      Click="menuItem_CopyUsername_Click"
                      Header="Copy Username">
                <MenuItem.Icon>
                    <Image Source="/mypgm;component/Images/Copy.png" />
                </MenuItem.Icon>
            </MenuItem>
            <MenuItem x:Name="menuItem_CopyPassword"
                      Click="menuItem_CopyPassword_Click"
                      Header="Copy Password">
                <MenuItem.Icon>
                    <Image Source="/mypgm;component/Images/addclip.png" />
                </MenuItem.Icon>
            </MenuItem>
            <Separator />
            <MenuItem x:Name="menuItem_DeleteCreds"
                      Click="menuItem_DeleteCreds_Click"
                      Header="Delete">
                <MenuItem.Icon>
                    <Image Source="/mypgm;component/Images/Delete.png" />
                </MenuItem.Icon>
            </MenuItem>
        </ContextMenu>
    </ListView.Resources>
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
        </Style>
    </ListView.ItemContainerStyle>
    

    然后在菜单项“单击事件”中,我添加了如下代码:

    private void menuItem_CopyUsername_Click(object sender, RoutedEventArgs e)
    {
        Clipboard.SetText(mySelectedItem.Username);
    }
    

    myselecteditem用于listview.selecteditem:

     <ListView x:Name="ListViewCreds" SelectedItem="{Binding mySelectedItem, UpdateSourceTrigger=PropertyChanged}" ....
    

    如果有帮助请打勾…

        3
  •  3
  •   Community Egal    7 年前

    你可能对以下问题的答案感兴趣 this SO question -我也有同样的问题,但我不满意使用mousedown事件来捕获点击的项目。有几个人用简单易懂的解决方案做出了回应,你可能会感兴趣。

    摘要:您可以使用数据上下文将项目传递给处理程序,或者使用命令+命令参数设置。