【问题标题】:ContextMenu on ListBox Item with DataTemplate带有 DataTemplate 的 ListBox 项上的 ContextMenu
【发布时间】:2010-09-19 15:20:32
【问题描述】:

我有一个包含不同类别项目的列表框。 DataTemplates 用于以适当的方式呈现这些对象。我想在这些类的 DataTemplates 中有不同的上下文菜单。

使用鼠标一切正常,但使用键盘我无法调出上下文菜单。

这可能是因为键盘焦点不在 DataTemplate 的内容上,而是在 ListBoxItem 上。

如何让 ListBoxItem 引用内容的 ContextMenu?

示例代码:

<Window x:Class="WpfApplication8.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:WpfApplication8"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate DataType="{x:Type my:Orange}">
        <TextBlock>
            Orange
            <TextBlock.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Peel"/>
                </ContextMenu>
            </TextBlock.ContextMenu>
        </TextBlock>
    </DataTemplate>
    <DataTemplate DataType="{x:Type my:Apple}">
        <TextBlock>
            Apple
            <TextBlock.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Uncore"/>
                </ContextMenu>
            </TextBlock.ContextMenu>
        </TextBlock>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ListBox ItemsSource="{Binding Fruits}"/>
</Grid>
</Window>


using System.Windows;
using System.Collections.ObjectModel;

namespace WpfApplication8
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Fruits = new ObservableCollection<Fruit>();
            Fruits.Add(new Apple());
            Fruits.Add(new Apple());
            Fruits.Add(new Orange());
            this.DataContext = this;
        }

        public ObservableCollection<Fruit> Fruits { get; set; }
    }

    public class Fruit
    {
    }

    public class Apple : Fruit
    {
    }

    public class Orange : Fruit
    {
    }
}

【问题讨论】:

    标签: wpf listbox datatemplate contextmenu


    【解决方案1】:

    我也有这个问题。阅读Bea Stollnitz' blog 给了我一个想法。

    我从资源中的这样一个数据模板开始:

    <ContextMenu x:Key="MyMenu">
        <MenuItem Header="A" />
        <MenuItem Header="B" />
        <MenuItem Header="C" />
    </ContextMenu>
    
    <DataTemplate x:Key="MyTemplateKey" DataType="{x:Type local:myType}">
       <TextBlock ContextMenu="{StaticResource MyMenu}" >
            <Run Text="{Binding Path=MyBindingPath}" FontSize="20" FontWeight="Bold" />
       </TextBlock>
    </DataTemplate>
    

    如上所述,这会导致键盘菜单键不调用上下文菜单,尽管右键单击确实有效。问题是上下文菜单需要在 ListBoxItem 上,而不是里面的模板。

    你好!

    <Style x:Key="ContextLBI" TargetType="{x:Type ListBoxItem}">
        <Setter Property="ContextMenu" Value="{StaticResource MyMenu}">
    
        </Setter>
    </Style>
    

    现在,只需从数据模板中删除 ContextMenu,然后在列表框中设置您的样式,如下所示:

    <ListBox ItemTemplate="{StaticResource MyTemplateKey}" 
             ItemContainerStyle="{StaticResource ContextLBI}"
    ... >
    </ListBox>
    

    【讨论】:

      【解决方案2】:

      这个人和你有类似的问题:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5737a331-2014-4e39-b87c-215ae6a7cdd4

      不要与焦点斗争,而是向列表框添加上下文菜单。将 ContextMenuOpening 事件处理程序添加到您的列表框。在该处理程序中,根据当前所选项目的数据上下文,以编程方式添加您需要的任何菜单项。

      【讨论】:

      • 列表框的上下文菜单在列表框的中间打开,而不是所选项目所在的位置。另外,我想在 XAML 中声明上下文菜单项,这使得全球化比将其放入代码中更容易。对于视障用户或出于其他原因避免使用鼠标的用户而言,键盘焦点至关重要。
      【解决方案3】:

      我找到了解决方案。在代码隐藏中,我将为每个 ListBoxItem 提供我从其可视子项中找到的上下文菜单。

      它使我可以将上下文菜单添加到各种类的 DataTemplates 中,从而为我提供我喜欢的多态性。我也更喜欢在 XAML 中声明菜单。它适用于键盘导航以及鼠标使用。

      代码可能已经放在附加属性中或为了优雅而放置。

      我添加了一个加载的事件处理程序和这段代码:

          void MainWindow_Loaded(object sender, RoutedEventArgs e)
          {
              foreach (var item in list.Items)
              {
                  ListBoxItem lbItem = list.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
                  lbItem.ContextMenu = FindContextMenu(lbItem);
              }
          }
      
          private ContextMenu FindContextMenu(DependencyObject depObj)
          {
              ContextMenu cm = depObj.GetValue(ContextMenuProperty) as ContextMenu;
              if (cm != null)
                  return cm;
              int children = VisualTreeHelper.GetChildrenCount(depObj);
              for (int i = 0; i < children; i++)
              {
                  cm = FindContextMenu(VisualTreeHelper.GetChild(depObj, i));
                  if(cm != null)
                      return cm;
              }
              return null;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多