2009-04-14 12 views
17

Tengo el siguiente GridView:WPF: Presentación de un menú contextual para los elementos de un 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> 

Ahora me gustaría mostrar un menú contextual de un clic derecho sobre un elemento acotado que me permitirá recuperar la elemento seleccionado cuando manejo el evento en el código detrás.

¿De qué manera puedo lograr esto?


[Actualización]

siguiente código Dennis Roche 's, ahora tengo esto:

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

Pero al correr, estoy recibiendo esta excepción:

No se puede agregar contenido del tipo 'System.Windows.Control s.ContextMenu ' a un objeto del tipo' System.Object '. Error en el objeto 'System.Windows.Controls.ContextMenu' en el archivo de marcado 'MusicRepo_Importer; component/controls/trackgridcontrol.xaml'.

¿Cuál es el problema?

+1

primer error que puedo ver es que se está ajustando el ItemContainerStyle dos veces: primero en un recurso y luego otra vez a nivel local. Además, el menú contextual debe ser un recurso. Parece ser un error con WPF. Actualizaré mi publicación original con una solución. – Dennis

Respuesta

19

Sí, agregue un ListView.ItemContainerStyle con el menú contextual.

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

NOTA: Es necesario hacer referencia a la ContextMenu como un recurso y no se puede definir de forma local.

Esto habilitará el menú contextual para toda la fila. :)

También veo que manejo el evento PreviewMouseLeftButtonDown para asegurarme de que el elemento esté enfocado (y es el elemento seleccionado actualmente cuando consulta el ListView). Descubrí que tenía que hacer esto al cambiar el enfoque entre las aplicaciones, esto puede no ser cierto en su caso.

Actualizado

En el código detrás de archivo que hay que andar arriba del árbol visual para encontrar el elemento de la lista de contenedores como la fuente original del evento puede ser un elemento de la plantilla de elementos (por ejemplo, un 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(); 
} 

El MyVisualTreeHelper que es utilizar un envoltorio que he escrito a caminar rápidamente el árbol visual. Un subconjunto se publica a continuación.

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

Espero que esta información adicional ayude.

Dennis

+0

Por favor, mira mi actualización publicada –

+1

Excelente, gracias por la ayuda mate. –

+0

¿Puede explicar lo que debe hacerse en PreviewMouseLeftButtonDown? – bendewey

3

Usted puede estar interesado en las respuestas para this SO question - que tenía la misma pregunta, pero no estaba satisfecho con el uso del evento MouseDown para capturar el elemento que se ha hecho clic sobre. Varias personas respondieron con soluciones simples y fáciles de comprender que podrían interesarle.

Resumen: Puede usar el contexto de datos para pasar el elemento al manejador, o un comando + configuración de parámetro de comando.

8

Dennis,

Amor del ejemplo, sin embargo no he encontrado ninguna necesidad de Visual ayudante árbol ...

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

A continuación, el interior de los eventos MenuItem_Click he añadido código que se parece a esto:

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

mySelectedItem se utiliza en el ListView.SelectedItem:

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

Por favor marque mí si ayuda ...

+0

¡Respuesta impresionante! Realmente muestra algunos trucos de WPF. –

Cuestiones relacionadas