带有DataTemplate的ListBox项上的ContextMenu

时间:2010-09-19 15:20:32

标签: wpf listbox datatemplate contextmenu

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

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

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

如何让ListBoxItem引用Content的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
    {
    }
}

3 个答案:

答案 0 :(得分:3)

我也有这个问题。阅读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上,而不是里面的模板。

嘿presto!

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

我找到了解决方案。在代码隐藏中,我将为每个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;
    }