Определение того, какой ListViewItem был нажат в ListView при выполнении ContextMenu MenuItem - PullRequest
3 голосов
/ 14 июля 2009

Я пытаюсь использовать контекстное меню в виде списка для запуска некоторого кода, требующего данных, из которого он произошел.

Я изначально только что сделал это:

XAML:

    <ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
      <ListView.Resources>
        <ContextMenu x:Key="resourceContextMenu">
            <MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click" />
        </ContextMenu>
      </ListView.Resources>
      <ListView.ItemContainerStyle>
          <Style TargetType="{x:Type ListViewItem}">
              <Setter Property="ContextMenu" Value="{StaticResource resourceContextMenu}" />
          </Style>
      </ListView.ItemContainerStyle>
 ...

C #:

    private void cmMetadata_Click(object sender, RoutedEventArgs e)
    {
      // code that needs item data here
    }

Но я обнаружил, что исходный элемент списка не был доступен таким образом.

Я читал некоторые тактики о том, как обойти это, например, перехват события MouseDown и установка личного поля для элемента списка, по которому щелкнули, но это не устраивает меня, так как это кажется немного хакерским для передачи данные вокруг этого пути. А WPF должен быть легким, верно? :) Я прочитал этот ТАК * вопрос и этот вопрос MSDN на форуме , но я все еще не уверен, как на самом деле это сделать, так как ни одна из этих статей не работает в моем дело. Есть ли лучший способ передать элемент, на который вы нажали, в контекстное меню?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 15 июля 2009

Аналогично ответу Чарли, но не требует изменений в XAML.

private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
    MenuItem menu = sender as MenuItem;
    ListViewItem lvi = lvResources.ItemContainerGenerator.ContainerFromItem(menu.DataContext) as ListViewItem;
}
3 голосов
/ 15 июля 2009

Что ж, в обработчике cmMetadata_Click вы можете просто запросить свойство lvResources.SelectedItem, поскольку lvResources будет доступен из файла с выделенным кодом, в котором находится обработчик нажатия. Это не элегантно, но будет работать.

Если вы хотите быть немного более элегантным, вы можете изменить место, где вы настраиваете ContextMenu. Например, вы можете попробовать что-то вроде этого:

<ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
 <ListView.Style>
  <Style TargetType="ListView">
   <Setter Property="ItemContainerStyle">
    <Setter.Value>
     <Style TargetType="{x:Type ListViewItem}">
      <Setter Property="Template">
       <Setter.Value>
        <ControlTemplate TargetType="{x:Type ListViewItem}">
         <TextBlock Text="{TemplateBinding Content}">
          <TextBlock.ContextMenu>
           <ContextMenu>
            <MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click" 
             DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
           </ContextMenu>
          </TextBlock.ContextMenu>
         </TextBlock>
        </ControlTemplate>
       </Setter.Value>
      </Setter>
     </Style>
    </Setter.Value>
   </Setter>
  </Style>
 </ListView.Style>
 <ListViewItem>One Item</ListViewItem>
 <ListViewItem>Another item</ListViewItem>
</ListView>

Для этого нужно подключить шаблон для вашего ListViewItem, а затем вы можете использовать удобный ярлык TemplatedParent, чтобы назначить ListViewItem для DataContext вашего элемента меню.

Теперь ваш код выглядит следующим образом:

private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
    MenuItem menu = sender as MenuItem;
    ListViewItem item = menu.DataContext as ListViewItem;
}

Очевидно, недостатком является то, что вам теперь нужно будет завершить шаблон для ListViewItem, но я уверен, что вы сможете найти тот, который удовлетворит ваши потребности довольно быстро.

1 голос
/ 15 июля 2009

Поэтому я решил попробовать и реализовать командное решение. Я очень доволен тем, как это работает сейчас.

Сначала создал мою команду:

public static class CustomCommands
{
    public static RoutedCommand DisplayMetadata = new RoutedCommand();
}

Затем в своем настраиваемом элементе управления списком я добавил новую привязку команды к конструктору:

public SortableListView()
{
    CommandBindings.Add(new CommandBinding(CustomCommands.DisplayMetadata, DisplayMetadataExecuted, DisplayMetadataCanExecute));
}

А также там добавлены обработчики событий:

public void DisplayMetadataExecuted(object sender, ExecutedRoutedEventArgs e)
{
    var nbSelectedItem = (MyItem)e.Parameter;

    // do stuff with selected item
}

public void DisplayMetadataCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
    e.Handled = true;
}

Я уже использовал селектор стилей для динамического назначения стилей элементам списка, поэтому вместо того, чтобы делать это в xaml, я должен установить привязку в коде позади. Вы можете сделать это и в xaml:

public override Style SelectStyle(object item, DependencyObject container)
{
    ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
    MyItem selectedItem = (MyItem)item;
    Style s = new Style();

    var listMenuItems = new List<MenuItem>();
    var mi = new MenuItem();
    mi.Header= "Get Metadata";
    mi.Name= "cmMetadata";
    mi.Command = CustomCommands.DisplayMetadata;
    mi.CommandParameter = selectedItem;
    listMenuItems.Add(mi);

    ContextMenu cm = new ContextMenu();
    cm.ItemsSource = listMenuItems;

    // Global styles
    s.Setters.Add(new Setter(Control.ContextMenuProperty, cm));

    // other style selection code

    return s;
}

Мне нравится ощущение этого решения гораздо лучше, чем пытаться установить поле при щелчке мыши и пытаться получить доступ к тому, что щелкнуло таким образом.

...