WPF Получить ссылочный элемент из ContextMenu в ListViewItem - PullRequest
3 голосов
/ 25 октября 2010

У меня есть ListView с ContextMenu на каждом ListViewItem, в котором есть событие Click, как я могу определить в обработчике событий, какой элемент был выбран в этом ContextMenu?Мне нужен предмет ID.

 <Style TargetType="{x:Type ListViewItem}">

.,.

 <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate TargetType="tv:TreeListViewItem">
                        <Grid>
                            <Grid.ContextMenu>
                                <ContextMenu>
                                    <MenuItem Header="Open in current tab" Click="MenuItemCurrentTab_Click"/>
                                    <MenuItem Header="Open in new tab" Click="MenuItemNewTab_Click"/>
                                </ContextMenu>
                            </Grid.ContextMenu>

Ответы [ 4 ]

6 голосов
/ 25 октября 2010

См. этот поток ..

Следуя так же, как ответ по ссылке, вы бы

<Grid.ContextMenu> 
    <ContextMenu> 
        <MenuItem Header="Open in current tab"
                  Click="MenuItemCurrentTab_Click"
                  CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>

...

private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
{
    MenuItem menuItem = sender as MenuItem;
    if (menuItem != null)
    {
        ContextMenu parentContextMenu = menuItem.CommandParameter as ContextMenu;
        if (parentContextMenu != null)
        {
            ListViewItem listViewItem = parentContextMenu.PlacementTarget as ListViewItem;
        }
    } 
}

UPDATE

Добавьте это, чтобы получить родительский ListViewItem из Grid

public T GetVisualParent<T>(object childObject) where T : Visual
{
    DependencyObject child = childObject as DependencyObject;
    while ((child != null) && !(child is T))
    {
        child = VisualTreeHelper.GetParent(child);
    }
    return child as T;
}

private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
{
    MenuItem menuItem = sender as MenuItem;
    if (menuItem != null)
    {
        ContextMenu parentContextMenu = menuItem.CommandParameter as ContextMenu;
        if (parentContextMenu != null)
        {
            Grid grid = parentContextMenu.PlacementTarget as Grid;
            ListViewItem listViewItem = GetVisualParent<ListViewItem>(grid);
        }
    } 
}
3 голосов
/ 25 октября 2010
    private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = (MenuItem)e.Source;
        ContextMenu menu = (ContextMenu)menuItem.Parent;
        ListViewItem item = (ListViewItem)menu.PlacementTarget;
        // do something with item
    }

Но, вероятно, лучше создать одно ContextMenu, дать ему правильное имя и использовать его для всех элементов представления списка.

1 голос
/ 07 апреля 2019

Повторяющаяся проблема, со многими попытками решить, но у всех есть свои недостатки. Например, принятый ответ предполагает, что каждый ListViewItem имеет свой ContextMenu. Это работает, но, особенно с большим количеством элементов списка, имеет значительную стоимость в сложности XAML и может быть медленным. И действительно не нужно вообще. Если мы используем только один ContextMenu на самом ListView, некоторые другие решения предлагают использовать

 <MenuItem CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />

, который, похоже, решает проблему с первого взгляда (PlacementTarget указывает на ListView, его SelectedItem указывает на элемент списка, поэтому обработчик элемента меню может использовать CommandParameter для получения исходного элемента списка ), но, к сожалению, происходит сбой, если в ListView разрешено множественное выделение (SelectedItem будет указывать на один из выбранных элементов, но не обязательно на тот, который был выбран в данный момент), или если мы используем ListView.PreviewMouseRightButtonDown для отключения выделения справа click (что, пожалуй, единственная логическая вещь, которую можно сделать с несколькими вариантами выбора).

Однако существует подход, который имеет все преимущества:

  • один ContextMenu на самом ListView;
  • работает со всеми схемами выбора, одиночной, множественной, отключенной;
  • даже с множественным выбором, он передаст в данный момент обработанный элемент обработчику.

Учтите это ListView:

<ListView ContextMenuOpening="ListView_ContextMenuOpening">
  <ListView.ContextMenu>
    <ContextMenu>
      <MenuItem Header="Menu1" Click="Menu1_Click" CommandParameter="{Binding Parent, RelativeSource={RelativeSource Self}}" />
    </ContextMenu>
  </ListView.ContextMenu>
</ListView>

CommandParameter используется для передачи родителя MenuItem, т.е. сам по себе ContextMenu. Но главный трюк в обработчике открытия меню:

private void ListView_ContextMenuOpening(object sender, ContextMenuEventArgs e) {
  var menu = (e.Source as FrameworkElement).ContextMenu;
  menu.Tag = (FrameworkElement)e.OriginalSource;
}

Внутри этого обработчика нам все еще известен исходный источник события, корень FrameworkElement элемента списка DataTemplate. Давайте сохраним его в Tag меню для последующего поиска.

private void Menu1_Click(object sender, RoutedEventArgs e) {
  if (sender is MenuItem menu)
    if (menu.CommandParameter is ContextMenu context)
      if (context.Tag is FrameworkElement item)
        if (item.DataContext is DataType data) {
          //process data
        }
}

В обработчике щелчка меню мы можем найти исходный ContextMenu, который мы сохранили в параметре команды, из которого мы можем найти корень FrameworkElement элемента списка, который мы сохранили ранее, и, наконец, получить объект, хранящийся в элементе списка (типа DataType).

0 голосов
/ 30 июля 2018
ListViewItem item = myListView.SelectedItem as ListViewItem;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...