[Edit # 3] - всем, кто читает этот вопрос: не ни при каких обстоятельствах используйте подход, изложенный в этом вопросе. Это кодовый ужас . Я свободно признаю это, зная, что все программисты загнали себя в угол в прошлом, и (особенно при изучении новой технологии) все мы были введены в заблуждение другими благонамеренными разработчиками в сети. Сначала прочитайте ответ Роберта, а затем прочитайте этот вопрос. Пожалуйста.
[Правка # 2b]
Я прошу прощения за длину этого вопроса - здесь есть вопрос (в конце!), Но я хотел убедиться, что исходный код был явным. Так или иначе.
[Редактировать # 2] - название вопроса изменено, чтобы более точно отразить ... вопрос.
[Редактировать] - Я обновил еще немного истории о том, как я оказался в дизайне / коде, который я сделал здесь: Обязательное сообщение в блоге . Если это поможет прояснить вопрос ниже, не стесняйтесь читать его ...
Оригинальный вопрос
Приложение, над которым я работаю, использует 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. Есть несколько отличных статей. В этой теме я рассмотрел следующие вопросы: Создание меню WPF с привязкой к данным с использованием HierarchicalDataTemplate и Серия примеров WPF - образец меню с привязкой к данным HierarchicalDataTemplate . Следуя совету в статье, мне удалось создать динамически построенное меню без явных проблем с привязкой данных - оно может создать меню с элементами, связанными с моей моделью представления, отражая структуру 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 и т. Д. Не отображаются. , Интересно, что это также приводит к удалению исходного текста в ContentPresent в HierarchicalDataTemplate.
Тогда возникает вопрос, есть ли способ установить свойство Header объекта MenuItem в событии Load так, чтобы оно правильно отображалось в UserControl? Является ли тот факт, что элементы в заголовке не отображаются, свидетельствует о проблеме привязки данных? Если это так, что будет правильным способом связать заголовок с временным объектом (StackPanel, который был создан в обработчике события загрузки)?
Я открыт для того, чтобы что-то изменить в приведенном выше коде - это все что-то вроде прототипирования, пытающегося найти лучший способ справиться с созданием динамического меню.
Спасибо!