WPF:Вложенные элементы меню на панели инструментов
Вопрос
Я работаю над некоторым XAML для приложения wpf, и у меня возникают некоторые проблемы с тем, чтобы заставить его делать то, что я хочу.Вот пример моего XAML:
<!-- Tool Bar Tray -->
<ToolBarTray Name="toolBarTray1" DockPanel.Dock="Top">
<!-- File And Edit Tools -->
<ToolBar Name="toolBar1" Band="1" BandIndex="1">
<!-- Regular Items -->
<Button>A</Button>
<Button>B</Button>
<!-- Overflow Menu For Special Items -->
<MenuItem ToolBar.OverflowMode="Always" Header="Special Items">
<MenuItem Header="C"/>
<MenuItem Header="D"/>
</MenuItem>
</ToolBar>
</ToolBarTray>
Когда я нажимаю на кнопку переполнения моей панели инструментов, появляется меню "Специальные элементы" с маленькой стрелкой рядом с ним, указывающей на вложенные элементы.Однако, когда я навожу курсор мыши на "Специальные элементы" или пытаюсь щелкнуть по нему, элементы меню "C" и "D" не отображаются.
Я надеялся, что MenuItem будет работать просто вне меню, но я попытался сделать все прямолинейно, на всякий случай.Включая эти элементы меню внутрь меню и, вместо этого, предоставляя этому меню панель инструментов.Свойство OverflowMode="Всегда" создает некоторый нежелательный стиль.Стрелки больше нет, для активации подменю необходимо нажать на пункт "Специальные пункты", а расположение подменю выглядит немного не так.
Кто-нибудь знает, что происходит?
Редактировать:Добавление меню к переполнению приводит к получению именно того, что я просил (большой сюрприз).То, что мне нужно, - это способ преобразования заголовков и элементов верхнего уровня на уровень подменю.Я обратился к этому примеру шаблона управления на MSDN для решения (ниже).
Править,Править:@gcores (обсуждение комментариев):Неужели?Я что-то упускаю?
<ToolBar Name="toolBar1" Band="1" BandIndex="4">
<!-- Displayed Buttons -->
<Button>A</Button>
<Button>B</Button>
<!-- Special Items Menu -->
<Menu ToolBar.OverflowMode="Always" >
<MenuItem Style="{StaticResource MenuItemStyle}" Header="Special">
<MenuItem Header="C"/>
<MenuItem Header="D"/>
</MenuItem>
</Menu>
</ToolBar>
Этот фрагмент у меня не работает.Я должен нажать на "Специальный", чтобы отобразилось подменю.
Решение
Единственный способ, который я смог найти, хотя бы приблизиться к созданию такого поведения, заключался в создании меню в переполнении, которое содержало бы один пункт меню, заголовок которого сам по себе был другим пунктом меню под названием «Специальные элементы» и содержал соответствующие дочерние элементы.Это работало, как задумано, но выглядело странно (это можно было исправить с помощью пользовательских шаблонов), а также выглядело как огромный хак.Единственный «правильный» способ сделать это, о котором я могу думать, - это создать свой собственный элемент управления, похожий на MenuItem, который отображает ContextMenu или Popup при наведении курсора, поскольку я не думаю, что пользовательский ControlTemplate может изменить поведение по умолчанию меню, чтобы не требовать щелчка по элементу верхнего уровня.
Другие советы
Другое решение — использовать существующие шаблоны и переопределить шаблон для TopLevelHeader шаблоном для SubmenuHeader.
<Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}">
<Style.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Template"
Value="{StaticResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}"/>
</Trigger>
</Style.Triggers>
</Style>
И используйте этот стиль в своем MenuItem верхнего уровня.Это должно упростить ваш код.
РЕДАКТИРОВАТЬ:Вы правы, это работает только тогда, когда вы нажимаете на него (не знаю, как я себя убедил, что это работает, извините :)).Его функциональность аналогична TopLevelMenu, хотя в шаблоне указано иное, это довольно сбивает с толку.
Единственное, о чем я могу думать, это добавить триггер для отображения подменю в IsMenuOver и обработать событие Click, чтобы оно ничего не делало, но я не знаю, насколько хорошо это будет работать.
После дополнительного прочтения ниже приведено решение, которое я использую.
<!-- Resource Dictionary Stuff -->
<!-- Some Brushes -->
<SolidColorBrush x:Key="Brush_1"
Color="White" />
<LinearGradientBrush x:Key="Brush_2"
StartPoint="0 0"
EndPoint="0 1">
<GradientStop
Color="White"
Offset="0"/>
<GradientStop
Color="DarkSeaGreen"
Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Brush_3"
Color="DarkOliveGreen"/>
<!-- Custom MenuItem - Top Level Header - Style 1 -->
<Style x:Key="MenuItem_TLH_Style1"
TargetType="MenuItem">
<!--<EventSetter Event="PreviewMouseDown" Handler="DoNothing"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="ControlTemplate"
TargetType="MenuItem">
<!-- A headered text that may display a submenu
on a trigger. This submenu is the host for a
menu item's items. -->
<Border x:Name="BoundaryBorder"
Background="{StaticResource Brush_1}"
BorderThickness="1">
<Grid x:Name="ContainerGrid">
<ContentPresenter x:Name="HeaderContent"
Margin="6 3 6 3"
ContentSource="Header"
RecognizesAccessKey="True"/>
<Popup x:Name="SubmenuPopup"
Placement="Bottom"
IsOpen="{TemplateBinding IsSubmenuOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border x:Name="SubmenuBoundaryBorder"
SnapsToDevicePixels="True"
Background="{StaticResource Brush_1}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1">
<StackPanel x:Name="ItemsStackPanel"
IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- -->
<Trigger
Property="IsSuspendingPopupAnimation"
Value="true">
<Setter
TargetName="SubmenuPopup"
Property="PopupAnimation"
Value="Fade"/>
</Trigger>
<!-- On mouse-over, show the submenu and highlight the header. -->
<Trigger
Property="IsMouseOver"
Value="true">
<Setter
TargetName="BoundaryBorder"
Property="Background"
Value="{StaticResource Brush_2}"/>
<Setter
TargetName="BoundaryBorder"
Property="BorderBrush"
Value="{StaticResource Brush_3}"/>
<Setter
Property="IsSubmenuOpen"
Value="true"/>
<!-- sloppy? -->
<Setter
TargetName="SubmenuPopup"
Property="IsOpen"
Value="true"/>
</Trigger>
<Trigger
SourceName="SubmenuPopup"
Property="AllowsTransparency"
Value="true">
<Setter
TargetName="SubmenuBoundaryBorder"
Property="CornerRadius"
Value="0 0 4 4"/>
<Setter
TargetName="SubmenuBoundaryBorder"
Property="Padding"
Value="0 0 0 3"/>
</Trigger>
<!-- Visually indicate an unaccessible menu item. -->
<Trigger
Property="IsEnabled"
Value="false">
<Setter
Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ... -->
<!-- Inside a window XAML file -->
<!-- Tool Bar Tray -->
<ToolBarTray x:Name="toolBarTray1"
DockPanel.Dock="Top">
<!-- File And Edit Tools -->
<ToolBar x:Name="toolBar1"
Band="1" BandIndex="1">
<!-- Displayed Buttons -->
<Button x:Name="ButtonA"
Content="A"/>
<Button x:Name="ButtonB"
Content="B"/>
<!-- Overflow Menu For Special Items -->
<Menu x:Name="OverflowMenu"
ToolBar.OverflowMode="Always">
<MenuItem x:Name="SpecialsMenuItem"
Style="{StaticResource MyStyle}"
Header="Special Items">
<MenuItem x:Name="CMenuItem"
Header="C">
<MenuItem x:Name="DMenuItem"
Header="D"/>
</MenuItem>
</MenuItem>
</Menu>
</ToolBar>
</ToolBarTray>
Я атакую поведение "SubmenuPopup" при наведении курсора мыши, вместо того чтобы обрабатывать событие click.Я хотел бы понять это более полно, поэтому я попытался закомментировать эту часть триггера и добавить обработчик события, который вызывает метод 'DoNothing()' для события 'PreviewMouseDown'.Оказывается, я чего-то не понимаю, и я думаю, что это связано с фокусировкой и / или с тем, как меню обрабатывает свою коллекцию элементов.Не позволяет событию распространяться после 'DoNothing()' (RoutedEventArgs.Обработано = true), похоже, устраняет проблемы при нажатии на пункт меню "Специальные пункты".Однако, если кто-то отошел от меню или добавил другой пункт меню, а затем нажал на него, режим наведения можно отключить или включать и выключать снова.