WPF: Displaying a Context Menu for a GridView's Items WPF: Displaying a Context Menu for a GridView's Items wpf wpf

WPF: Displaying a Context Menu for a GridView's Items


Yes, add a ListView.ItemContainerStyle with the Context Menu.

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

NOTE: You need to reference the ContextMenu as a resource and cannot define it locally.

This will enable the context menu for the entire row. :)

Also see that I handle the PreviewMouseLeftButtonDown event so I can ensure the item is focused (and is the currently selected item when you query the ListView). I found that I had to this when changing focus between applications, this may not be true in your case.

Updated

In the code behind file you need to walk-up the visual tree to find the list container item as the original source of the event can be an element of the item template (e.g. a 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();}

The MyVisualTreeHelper that is use a wrapper that I've written to quickly walk the visual tree. A subset is posted below.

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

I hope this additional information helps.

Dennis


Dennis,

Love the example, however I did not find any need for your Visual Tree Helper...

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

Then inside the MenuItem_Click events I added code that looks like this:

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

mySelectedItem is used on the ListView.SelectedItem:

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

Please tick me if it helps...


You might be interested in the answers for this SO question - I had the same question but wasn't satisfied with using the mousedown event to capture the item that was clicked upon. Several people has responded with simple and easy to comprehend solutions that you might be interested in.

Summary : You can use the data context to pass the item through to the handler, or a command + command parameter setup.