2009-04-14 30 views
17

Ho il seguente GridView:WPF: Visualizzazione di un menu contestuale articoli di 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> 

Ora vorrei per visualizzare un menu contestuale su un clic destro su un elemento limitato che mi permetterà di recuperare il elemento selezionato quando gestisco l'evento nel codice sottostante.

In che modo possibile posso realizzare questo?


[Update]

Seguendo codice Dennis Roche 's, ora ho questo:

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

Ma su corsa, sto ricevendo questa eccezione:

Impossibile aggiungere il contenuto di tipo 'System.Windows.Control s.ContextMenu ' a un oggetto di tipo' System.Object '. Errore nell'oggetto 'System.Windows.Controls.ContextMenu' nel file di markup 'MusicRepo_Importer; component/controls/trackgridcontrol.xaml'.

Qual è il problema?

+1

primo errore che posso vedere è che si sta impostando l'ItemContainerStyle due volte: prima a una risorsa e poi di nuovo a livello locale. Inoltre, il menu di scelta rapida deve essere una risorsa. Sembra essere un bug con WPF. Aggiornerò il mio post originale con una soluzione. – Dennis

risposta

19

Sì, aggiungere ListView.ItemContainerStyle con il menu di scelta rapida.

<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: è necessario fare riferimento a ContextMenu come risorsa e non definirlo localmente.

Ciò abiliterà il menu di scelta rapida per l'intera riga. :)

Si noti inoltre che gestisco l'evento PreviewMouseLeftButtonDown in modo da garantire che l'elemento sia focalizzato (ed è l'elemento attualmente selezionato quando si esegue una query su ListView). Ho scoperto che dovevo farlo cambiando il focus tra le applicazioni, questo potrebbe non essere vero nel tuo caso.

Aggiornato

Nel codice dietro file è necessario per walk-up albero visuale per trovare l'elemento della lista contenitore come la fonte originale della manifestazione può essere un elemento del modello di elemento (ad esempio uno 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(); 
} 

Il MyVisualTreeHelper che è utilizzare un wrapper che ho scritto a camminare rapidamente l'albero visuale. Un sottoinsieme è pubblicato sotto.

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

Spero che questa ulteriore informazione sia d'aiuto.

Dennis

+0

Si prega di consultare il mio aggiornamento postato –

+1

Eccellente, grazie per l'aiuto amico. –

+0

Puoi approfondire cosa è necessario fare su PreviewMouseLeftButtonDown? – bendewey

3

Potreste essere interessati alle risposte per this SO question - ho avuto la stessa domanda, ma non ero soddisfatto utilizzando l'evento MouseDown di catturare l'elemento che è stato fatto clic su. Diverse persone hanno risposto con soluzioni semplici e facili da comprendere a cui potresti essere interessato.

Riepilogo: è possibile utilizzare il contesto dati per passare l'elemento al gestore o un comando + comando impostazione parametri.

8

Dennis,

Amore l'esempio, però non ho trovato alcun bisogno di Visual Albero 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> 

Poi all'interno degli eventi MenuItem_Click ho aggiunto il codice che assomiglia a questo:

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

mySelectedItem viene utilizzato sul ListView.SelectedItem:

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

Barrare me se aiuta ...

+0

Risposta stupenda! Mostra davvero alcuni trucchi WPF. –

Problemi correlati