代码之家  ›  专栏  ›  技术社区  ›  Guge

带有数据模板的列表框项的ContextMenu

  •  2
  • Guge  · 技术社区  · 15 年前

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

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

    这可能是因为键盘焦点不在数据模板的内容上,而在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
        {
        }
    }
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Anthony Wieser    15 年前

    我也有这个问题。阅读 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
  •  1
  •   Wallstreet Programmer    15 年前

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

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

        3
  •  0
  •   Guge    15 年前

    我找到了解决办法。在后面的代码中,我将为每个ListBoxItem提供从其可视化子项中找到的上下文菜单。

    它使我能够为各种类向数据模板中添加上下文菜单,从而提供我喜欢的多态性。我还喜欢用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;
        }
    
    推荐文章