首页 \ 问答 \ 如何动态添加MenuItems(带标题)到WPF菜单(How to dynamically add MenuItems (with a header) to a WPF menu)

如何动态添加MenuItems(带标题)到WPF菜单(How to dynamically add MenuItems (with a header) to a WPF menu)

[编辑#3] - 给阅读此问题的任何人:在任何情况下都不要使用此问题中概述的方法。 这是一个编码恐怖 。 我毫不犹豫地承认这一点,知道所有程序员都在过去的一个角落工作,并且(尤其是在学习新技术的时候),我们都被互联网上的其他好的开发人员误导了。 先阅读罗伯特的答案,然后阅读这个问题。 请。

[编辑#2b]

我对这个问题的长度表示歉意 - 这里有一个问题(最后!),但我想确保源代码是明确的。 无论如何。

[编辑#2] - 问题标题更改为更准确地反映...问题。

[编辑] - 我已经更新了一些关于我如何结束在这里所做的设计/代码的历史: Obligatory Blog Post 。 如果它有助于澄清下面的问题,请随时阅读它...

原始问题

我正在使用的应用程序使用Prism和WPF,并带有许多模块(当前为3个),其中一个模块托管应用程序菜单。 最初,该菜单是静态的,可以挂接到CompositeCommand / DelegateCommands中,这对于将按钮路由到相应的演示者非常有用。 每个MenuItem在其标题中使用StackPanel将内容显示为图像和文本标签的组合 - 这正是我期待的目的:

<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent">
                <MenuItem Name="MenuFile" AutomationProperties.AutomationId="File">
                    <MenuItem.Header>
                        <StackPanel>
                            <Image Height="24" VerticalAlignment="Center" Source="../Resources/066.png"/>
                            <ContentPresenter Content="Main"/>
                        </StackPanel>
                    </MenuItem.Header>
                    <MenuItem AutomationProperties.AutomationId="FileExit" Command="{x:Static local:ToolBarCommands.FileExit}">
                        <MenuItem.Header>
                            <StackPanel>
                                <Image Height="24" VerticalAlignment="Center" Source="../Resources/002.png"/>
                                <ContentPresenter Content="Exit"/>
                            </StackPanel>
                        </MenuItem.Header>
                    </MenuItem>
                </MenuItem>
                <MenuItem  Name="MenuHelp" AutomationProperties.AutomationId="Help" Command="{x:Static local:ToolBarCommands.Help}">
                    <MenuItem.Header>
                        <StackPanel>
                            <Image Height="24" VerticalAlignment="Center" Source="../Resources/152.png"/>
                            <ContentPresenter Content="Help"/>
                        </StackPanel>
                    </MenuItem.Header>
                </MenuItem>               
            </Menu>

不幸的是,应用程序已经变得更复杂了,希望让其他模块向菜单注册 - 因此,我一直在研究使菜单动态化。 目标是让其他模块(通过服务)能够随意向菜单添加命令 - 例如,模块A将在工具栏模块中添加一个菜单项,以调用模块A中的处理程序。有几篇优秀的文章在这个主题上 - 我看过的两个是使用HierarchicalDataTemplateWPF示例系列 - Databound HierarchicalDataTemplate菜单示例 构建Databound WPF菜单 。 按照文章中的建议,我设法制作了一个动态构建的菜单,没有明显的数据绑定问题 - 它可以创建一个菜单项,并链接到我的演示文稿模型,反映了演示模型中ObservableCollection的结构

目前,我的XAML如下所示:

<UserControl x:Class="Modules.ToolBar.Views.ToolBarView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:model="clr-namespace:Modules.ToolBar.PresentationModels"
    xmlns:local="clr-namespace:Modules.ToolBar">
    <UserControl.Resources>
        <model:ToolBarPresentationModel x:Key="modelData" />
        <HierarchicalDataTemplate DataType="{x:Type model:ToolbarObject}"
                                  ItemsSource="{Binding Path=Children}">
            <ContentPresenter Content="{Binding Path=Name}"
                              Loaded="ContentPresenter_Loaded"
                              RecognizesAccessKey="True"/>
        </HierarchicalDataTemplate>
    </UserControl.Resources>
    <UserControl.DataContext>
        <Binding Source="{StaticResource modelData}"/>
    </UserControl.DataContext>
        <Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent"
            ItemsSource="{Binding}">
        </Menu>
    </Grid>
</UserControl>

该视图背后的代码在ContentPresenter_Loaded方法中负担过重:

   private void ContentPresenter_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
    ContentPresenter presenter = sender as ContentPresenter;
    if (sender != null)
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(presenter);
        bool bContinue = true;
        while (bContinue
            || parentObject == null)
        {
            if (parentObject is MenuItem)
                bContinue = false;
            else
                parentObject = VisualTreeHelper.GetParent(parentObject);
        }
        var menuItem = parentObject as MenuItem;
        if (menuItem != null)
        {
            ToolbarObject toolbarObject = menuItem.DataContext as ToolbarObject;
            StackPanel panel = new StackPanel();
            if (!String.IsNullOrEmpty(toolbarObject.ImageLocation))
            {
                Image image = new Image();
                image.Height = 24;
                image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                Binding sourceBinding = new Binding("ImageLocation");
                sourceBinding.Mode = BindingMode.TwoWay;
                sourceBinding.Source = toolbarObject;
                image.SetBinding(Image.SourceProperty, sourceBinding);
                panel.Children.Add(image);
            }
            ContentPresenter contentPresenter = new ContentPresenter();
            Binding contentBinding = new Binding("Name");
            contentBinding.Mode = BindingMode.TwoWay;
            contentBinding.Source = toolbarObject;
            contentPresenter.SetBinding(ContentPresenter.ContentProperty, contentBinding);
            panel.Children.Add(contentPresenter);
            menuItem.Header = panel;
            Binding commandBinding = new Binding("Command");
            commandBinding.Mode = BindingMode.TwoWay;
            commandBinding.Source = toolbarObject;
            menuItem.SetBinding(MenuItem.CommandProperty, commandBinding);                    
        }
    }
}

正如你所看到的,我试图重新创建原始菜单的StackPanel / Image / Name组合,只是在后面的代码中这样做。 试图做到这一点并没有那么成功 - 虽然菜单对象当然是被创建的,但它们不会像空白的可点击对象那样“出现” - StackPanel,Image,Name等不会被呈现。 有趣的是,它也导致HierarchicalDataTemplate中的ContentPresent中的原始文本被删除。

那么问题是,有没有办法在Load事件中设置MenuItem的Header属性,以便它能正确显示在UserControl上? 事实上,标题中的项目没有被显示,指示DataBinding问题? 如果是这样,将Header绑定到一个临时对象(在load事件处理程序中创建的StackPanel)的正确方法是什么?

我愿意改变上述代码中的任何东西 - 这是各种原型设计,试图找出处理动态菜单创建的最佳方式。 谢谢!


[Edit #3] - to anyone reading this question: do not under any circumstance use the approach outlined in this question. It is a Coding Horror. I freely admit this, knowing that all programmers have worked themselves into a corner in the past, and (especially when learning a new technology) we all have been led astray by other, well-meaning developers on the interweb. Read the answer by Robert first, then read this question. Please.

[Edit #2b]

I apologize for the length of this question - there is a question in here (at the end!), but I wanted to make sure the source code was explicit. Anyway.

[Edit #2] - question title changed to more accurately reflect the... question.

[Edit] - I've updated some more of the history as to how I ended up at the design / code that I did here: Obligatory Blog Post. If it helps clarify the question below, feel free to read it...

Original question

The application I'm working on uses Prism and WPF, with a number of modules (currently 3), one of which hosts the application menu. Originally, the menu was static with hooks into CompositeCommand / DelegateCommands, which worked great for routing button presses to the appropriate presenter. Each MenuItem used a StackPanel in its header to display the content as a combination of an image and a text label - which was the look I was going for:

<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent">
                <MenuItem Name="MenuFile" AutomationProperties.AutomationId="File">
                    <MenuItem.Header>
                        <StackPanel>
                            <Image Height="24" VerticalAlignment="Center" Source="../Resources/066.png"/>
                            <ContentPresenter Content="Main"/>
                        </StackPanel>
                    </MenuItem.Header>
                    <MenuItem AutomationProperties.AutomationId="FileExit" Command="{x:Static local:ToolBarCommands.FileExit}">
                        <MenuItem.Header>
                            <StackPanel>
                                <Image Height="24" VerticalAlignment="Center" Source="../Resources/002.png"/>
                                <ContentPresenter Content="Exit"/>
                            </StackPanel>
                        </MenuItem.Header>
                    </MenuItem>
                </MenuItem>
                <MenuItem  Name="MenuHelp" AutomationProperties.AutomationId="Help" Command="{x:Static local:ToolBarCommands.Help}">
                    <MenuItem.Header>
                        <StackPanel>
                            <Image Height="24" VerticalAlignment="Center" Source="../Resources/152.png"/>
                            <ContentPresenter Content="Help"/>
                        </StackPanel>
                    </MenuItem.Header>
                </MenuItem>               
            </Menu>

Unfortunately, the application has gotten a bit more complex and it is desireable to have other modules register themselves with the menu - hence, I've been looking at making the menu dynamic. The goal is to have other modules (through a service) be able to add commands to the menu at will - for example, Module A will add a menu item in the Toolbar module that calls a handler in Module A. There's a few excellent articles out there on this subject - the two I've looked at are Building a Databound WPF Menu Using a HierarchicalDataTemplate and WPF Sample Series - Databound HierarchicalDataTemplate Menu Sample. Following the advice in the article, I have managed to make a dynamically constructed menu with no obvious data binding problems - it can create a menu with items linked backed to my presentation model, reflecting the structure of an ObservableCollection in the presentation model

Currently, my XAML looks like the following:

<UserControl x:Class="Modules.ToolBar.Views.ToolBarView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:model="clr-namespace:Modules.ToolBar.PresentationModels"
    xmlns:local="clr-namespace:Modules.ToolBar">
    <UserControl.Resources>
        <model:ToolBarPresentationModel x:Key="modelData" />
        <HierarchicalDataTemplate DataType="{x:Type model:ToolbarObject}"
                                  ItemsSource="{Binding Path=Children}">
            <ContentPresenter Content="{Binding Path=Name}"
                              Loaded="ContentPresenter_Loaded"
                              RecognizesAccessKey="True"/>
        </HierarchicalDataTemplate>
    </UserControl.Resources>
    <UserControl.DataContext>
        <Binding Source="{StaticResource modelData}"/>
    </UserControl.DataContext>
        <Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent"
            ItemsSource="{Binding}">
        </Menu>
    </Grid>
</UserControl>

The code behind for the view does the heavy lifting in the ContentPresenter_Loaded method:

   private void ContentPresenter_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
    ContentPresenter presenter = sender as ContentPresenter;
    if (sender != null)
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(presenter);
        bool bContinue = true;
        while (bContinue
            || parentObject == null)
        {
            if (parentObject is MenuItem)
                bContinue = false;
            else
                parentObject = VisualTreeHelper.GetParent(parentObject);
        }
        var menuItem = parentObject as MenuItem;
        if (menuItem != null)
        {
            ToolbarObject toolbarObject = menuItem.DataContext as ToolbarObject;
            StackPanel panel = new StackPanel();
            if (!String.IsNullOrEmpty(toolbarObject.ImageLocation))
            {
                Image image = new Image();
                image.Height = 24;
                image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                Binding sourceBinding = new Binding("ImageLocation");
                sourceBinding.Mode = BindingMode.TwoWay;
                sourceBinding.Source = toolbarObject;
                image.SetBinding(Image.SourceProperty, sourceBinding);
                panel.Children.Add(image);
            }
            ContentPresenter contentPresenter = new ContentPresenter();
            Binding contentBinding = new Binding("Name");
            contentBinding.Mode = BindingMode.TwoWay;
            contentBinding.Source = toolbarObject;
            contentPresenter.SetBinding(ContentPresenter.ContentProperty, contentBinding);
            panel.Children.Add(contentPresenter);
            menuItem.Header = panel;
            Binding commandBinding = new Binding("Command");
            commandBinding.Mode = BindingMode.TwoWay;
            commandBinding.Source = toolbarObject;
            menuItem.SetBinding(MenuItem.CommandProperty, commandBinding);                    
        }
    }
}

As you can see, I'm attempting to recreate the StackPanel / Image / Name combination of the original menu, just doing so in the code behind. Attempting to do this has not worked out so well - while the menu objects are certainly being created, they don't "appear" as anything other than blank, clickable objects - the StackPanel, Image, Name, etc. aren't being rendered. Interestingly enough, it also is causing the original text in the ContentPresent in the HierarchicalDataTemplate to be erased.

The question then, is there a way to set a MenuItem's Header property in the Load event such that it will display on the UserControl properly? Is the fact that the items in the header are not being displayed indicative of a DataBinding problem? If so, what would be the proper way to bind the Header to a transient object (the StackPanel that was created in the load event handler)?

I'm open to changing anything in the code above - this is all sort of prototyping along, trying to figure out the best way to handle dynamic menu creation. Thanks!


原文:https://stackoverflow.com/questions/3723580
更新时间:2023-10-09 20:10

最满意答案

GCC libstdc++在您编译的任何版本中都不支持这些C ++ 11操纵符。 一个月前提交了一个补丁


GCC libstdc++ just doesn't support these C++11 manipulators in any of the versions you've compiled against. A patch was submitted exactly one month ago

相关问答

更多

相关文章

更多

最新问答

更多
  • 获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)
  • 如何通过引用返回对象?(How is returning an object by reference possible?)
  • 矩阵如何存储在内存中?(How are matrices stored in memory?)
  • 每个请求的Java新会话?(Java New Session For Each Request?)
  • css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)
  • 无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)
  • xcode语法颜色编码解释?(xcode syntax color coding explained?)
  • 在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)
  • 从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)
  • 从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))
  • 西安哪有PLC可控制编程的培训
  • 在Entity Framework中选择基类(Select base class in Entity Framework)
  • 在Android中出现错误“数据集和渲染器应该不为null,并且应该具有相同数量的系列”(Error “Dataset and renderer should be not null and should have the same number of series” in Android)
  • 电脑二级VF有什么用
  • Datamapper Ruby如何添加Hook方法(Datamapper Ruby How to add Hook Method)
  • 金华英语角.
  • 手机软件如何制作
  • 用于Android webview中图像保存的上下文菜单(Context Menu for Image Saving in an Android webview)
  • 注意:未定义的偏移量:PHP(Notice: Undefined offset: PHP)
  • 如何读R中的大数据集[复制](How to read large dataset in R [duplicate])
  • Unity 5 Heighmap与地形宽度/地形长度的分辨率关系?(Unity 5 Heighmap Resolution relationship to terrain width / terrain length?)
  • 如何通知PipedOutputStream线程写入最后一个字节的PipedInputStream线程?(How to notify PipedInputStream thread that PipedOutputStream thread has written last byte?)
  • python的访问器方法有哪些
  • DeviceNetworkInformation:哪个是哪个?(DeviceNetworkInformation: Which is which?)
  • 在Ruby中对组合进行排序(Sorting a combination in Ruby)
  • 网站开发的流程?
  • 使用Zend Framework 2中的JOIN sql检索数据(Retrieve data using JOIN sql in Zend Framework 2)
  • 条带格式类型格式模式编号无法正常工作(Stripes format type format pattern number not working properly)
  • 透明度错误IE11(Transparency bug IE11)
  • linux的基本操作命令。。。