2009-04-14 4 views
17

У меня есть следующие GridView:WPF: Отображение контекстного меню для единиц продукции в 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> 

Теперь я хотел бы, чтобы отобразить на правой кнопкой мыши на ограниченной элемент, который позволит мне извлечь контекстное меню элемент, выбранный, когда я обрабатываю событие в коде позади.

Каким возможным способом я могу это осуществить?


[Update]

После кода 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> 

Но после запуска, я получаю это исключение:

Невозможно добавить содержимое типа 'System.Windows.Control s.ContextMenu ' - объекту типа' System.Object '. Ошибка в объекте 'System.Windows.Controls.ContextMenu' в файле разметки 'MusicRepo_Importer; компонент/элементы управления/trackgridcontrol.xaml'.

В чем проблема?

+1

Первая ошибка, которую я вижу в том, что вы устанавливаете ItemContainerStyle дважды: первый к ресурсу, а затем снова на месте. Кроме того, контекстное меню должно быть ресурсом. Кажется, это ошибка с WPF. Я обновляю свое оригинальное сообщение с помощью решения. – Dennis

ответ

19

Да, добавьте 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); 
    } 
} 

Надеюсь, эта дополнительная информация поможет.

Dennis

+0

См. Мое опубликованное обновление –

+1

Отлично, спасибо за помощь помощнику. –

+0

Можете ли вы рассказать о том, что нужно сделать в PreviewMouseLeftButtonDown? – bendewey

3

Вы могли бы быть заинтересованы в ответах на this SO question - у меня был тот же вопрос, но не был удовлетворен, используя MouseDown событие, чтобы захватить пункт, который нажал на. Несколько человек ответили простым и легким для понимания решениями, которые могут вас заинтересовать.

Описание: Вы можете использовать контекст данных для передачи элемента через обработчик или настройку параметра команды +.

8

Деннис,

Любовь пример, но я не нашел никакой необходимости для вашего визуального дерева 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> 

Тогда внутри событий MenuItem_Click я добавил код, который выглядит следующим образом:

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}" .... 

Пожалуйста, отметьте меня, если это помогает ...

+0

Удивительный ответ! На самом деле показывает некоторые трюки WPF. –

Смежные вопросы