[编辑3]
-对于任何阅读这个问题的人:做
不
在任何情况下,使用本问题中概述的方法。
这是一个编码恐怖
. 我自由地承认这一点,因为我知道过去所有的程序员都在一个角落里工作,而且(尤其是在学习新技术的时候),我们都被互联网上其他善意的开发人员引入了歧途。先读罗伯特的答案,然后读这个问题。拜托。
[编辑2b]
我为这个问题的长度道歉-这里有个问题(结尾!)但我想确保源代码是明确的。不管怎样。
[编辑2]
-问题标题更改为更准确地反映…问题。
[编辑]
-我已经更新了更多的历史,关于我是如何最终得到我在这里所做的设计/代码的:
Obligatory Blog Post
. 如果它有助于澄清下面的问题,请随意阅读…
原始问题
我正在开发的应用程序使用prism和wpf,其中包含许多模块(当前为3个),其中一个模块承载应用程序菜单。最初,菜单是静态的,带有到compositecommand/delegatecommands的钩子,这对于将按钮按到适当的演示者非常有用。每个菜单项在其标题中使用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中调用一个处理程序。有一些关于这个主题的优秀文章-我看过的两篇是
Building a Databound WPF Menu Using a HierarchicalDataTemplate
和
WPF Sample Series - Databound HierarchicalDataTemplate Menu Sample
. 按照本文中的建议,我成功地创建了一个动态构建的菜单,没有明显的数据绑定问题——它可以创建一个菜单,其中项目链接到我的展示模型,反映了展示模型中可观察到的集合的结构。
当前,我的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加载方法中起重作用:
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、图像、名称等。有趣的是,它还导致层次结构数据模板中存在的内容中的原始文本被擦除。
那么,问题是,有没有一种方法可以在加载事件中设置menuitem的header属性,使其正确显示在用户控件上?是否显示标题中的项表示数据绑定问题?如果是这样,将头绑定到临时对象(在加载事件处理程序中创建的stackpanel)的正确方法是什么?
我愿意更改上面代码中的任何内容——这是所有类型的原型设计,试图找出处理动态菜单创建的最佳方法。
谢谢!