书接上回,我们的Fluent WPF的版图已经完成了:
- Fluent Window: WPF 模拟UWP原生窗口样式——亚克力|云母材质、自定义标题栏样式、原生DWM动画 (附我封装好的类)
- Fluent Popup & ToolTip: WPF中为Popup和ToolTip使用WindowMaterial特效 win10/win11
- Fluent ScrollViewer: WPF 使用CompositionTarget.Rendering实现平滑流畅滚动的ScrollViewer,支持滚轮、触控板、触摸屏和笔
先来看看效果图(win11):
有以下xaml代码:
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu Item 1" Icon="Cd" InputGestureText="aa?" />
<MenuItem Header="Menu Item 2" >
<MenuItem Header="Child Item 1" Icon="Ab" />
<MenuItem Header="Child Item 2" Icon="Ad" />
<MenuItem Header="Child Item 3" IsCheckable="True" IsChecked="True"/>
</MenuItem>
<MenuItem Header="Menu Item 3" Icon="cd" />
</ContextMenu>
</TextBlock.ContextMenu>

(由于我的win10虚拟机坏了,暂时没有测试)
前面的工作已经解决了让任意窗口支持Acrylic材质的问题,本文重点介绍ContentMenu和MenuItem的样式适配和实现。
本文的Demo:
一、为什么需要一个新的ContextMenu和MenuItem样式
如果你直接给ContextMenu应用WindowMaterial特效,可能会出现以下丑陋的效果:
或者:
原因在于古老的ContextMenu和MenuItem样式并不能通过简单修改Background实现我们想要的布局和交互效果。
二、ContextMenu与MenuItem的结构
1. ContextMenu 的结构
ContextMenu直观上看是一个Popup,但它的控件模板并不包含Popup,而是直接指定内部元素(包含一个ScrollViewer和StackPanel)。因此不能从控件模板中替换Popup为自定义的FluentPopup,需要使用其他手段让其内部Popup也支持Acrylic材质(见下文)。
2. MenuItem 的四种形态
在示例代码仓库中展示了较为完整的Menu相关的结构: 
MenuItem根据其在菜单树中的位置,通过Role属性分为四种形态。我们在Style.Triggers中分别为它们指定了不同的模板:
- TopLevelHeader: 顶级菜单项,且包含子菜单
Popup(例如菜单栏上的”File”)。 - TopLevelItem: 顶级菜单项,不含子菜单(例如菜单栏上的”Help”)。
- SubmenuHeader: 子菜单项,且包含下一级子菜单
Popup。 - SubmenuItem: 子菜单项,不含子菜单(叶子节点)。其模板主要处理图标、文字、快捷键的布局。
三、重写Menu相关控件模板和样式
1. ContextMenu 样式
ContextMenu的样式主要参考了.NET 9自带的Fluent样式,并作了一些调整以适配Acrylic材质。主要目的是覆盖原始模板的Icon部分白框和分割线:
因为我们的WindowMaterial已经为窗口自动附加上圆角、阴影和亚克力材质的DWM效果,所以我们只需要将ContextMenu的背景设置为透明,并移除不必要的边框和分割线即可。
此外,还添加了弹出动画(是在Popup内部做的,并非对window,效果可能不会很理想)。
2. MenuItem 样式
MenuItem的样式主要处理了图标、文字、快捷键的布局,并根据不同的角色(TopLevelHeader、TopLevelItem、SubmenuHeader、SubmenuItem)应用不同的模板。
- TopLevelHeader: 只包含Icon和Header,以及弹出的Popup,只需要替换为自定义的FluentPopup即可。
- TopLevelItem: 只包含Icon和Header,无Popup。
- SubmenuHeader: 包含Icon、Header和Chevron图标(这里就是一个展开的箭头图标,但是官方叫做雪佛龙..?),以及弹出的Popup,同样替换为FluentPopup。
- SubmenuItem: 叶子节点,包含Icon、Header和InputGestureText。 其模板主要处理图标、文字、快捷键的布局。
菜单项主要分为三个部分:Icon图标、Header文字和最右侧的提示文字或展开箭头图标。只需要保持三个部分的布局对其即可。如果IsCheckable为True,则Icon部分被自定义图标占据(√, 当IsChecked为True时)。
以下是完整的资源字典,包含了完整的注释。
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WindowEffectTest"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<!-- 菜单边框内边距 -->
<Thickness x:Key="MenuBorderPadding">0,3,0,3</Thickness>
<!-- 菜单项外边距 -->
<Thickness x:Key="MenuItemMargin">4,1</Thickness>
<!-- 菜单项内容内边距 -->
<Thickness x:Key="MenuItemContentPadding">10,6</Thickness>
<!-- 顶级菜单项外边距 -->
<Thickness x:Key="TopLevelItemMargin">4</Thickness>
<!-- 顶级菜单项内容边距 -->
<Thickness x:Key="TopLevelContentMargin">10</Thickness>
<!-- 菜单圆角半径 -->
<CornerRadius x:Key="MenuCornerRadius">4</CornerRadius>
<!-- 顶级菜单圆角半径 -->
<CornerRadius x:Key="TopLevelCornerRadius">6</CornerRadius>
<!-- 菜单动画持续时间 -->
<Duration x:Key="MenuAnimationDuration">0:0:0.167</Duration>
<!-- 复选标记图标路径数据 -->
<PathGeometry x:Key="CheckGraph">
M392.533333 806.4L85.333333 503.466667l59.733334-59.733334 247.466666 247.466667L866.133333 213.333333l59.733334 59.733334L392.533333 806.4z
</PathGeometry>
<!-- 前进箭头图标路径数据(用于子菜单指示器) -->
<PathGeometry x:Key="ForwardGraph">
M283.648 174.081l57.225-59.008 399.479 396.929-399.476 396.924-57.228-59.004 335.872-337.92z
</PathGeometry>
<!-- 菜单项ScrollViewer样式 -->
<Style
x:Key="MenuItemScrollViewerStyle"
BasedOn="{StaticResource {x:Type ScrollViewer}}"
TargetType="{x:Type ScrollViewer}">
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
</Style>
<!-- 默认集合焦点视觉样式 得到键盘焦点时显示 -->
<Style x:Key="DefaultCollectionFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle
Margin="4,0"
RadiusX="4"
RadiusY="4"
SnapsToDevicePixels="True"
Stroke="{DynamicResource AccentColor}"
StrokeThickness="2" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 默认上下文菜单样式 -->
<Style x:Key="DefaultContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="MinWidth" Value="140" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="HasDropShadow" Value="False" />
<Setter Property="Grid.IsSharedSizeScope" Value="True" />
<Setter Property="Popup.PopupAnimation" Value="None" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border
x:Name="Border"
Padding="{StaticResource MenuBorderPadding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<!-- 用于动画的转换变换 -->
<Border.RenderTransform>
<TranslateTransform />
</Border.RenderTransform>
<ScrollViewer CanContentScroll="True" Style="{StaticResource MenuItemScrollViewerStyle}">
<!-- 菜单项容器 -->
<StackPanel
ClipToBounds="True"
IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Cycle"
Orientation="Vertical" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<!-- 菜单打开时的动画效果 -->
<Trigger Property="IsOpen" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Y轴平移动画:从-45向下滑入到0 -->
<DoubleAnimation
Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.Y)"
From="-45"
To="0"
Duration="{StaticResource MenuAnimationDuration}">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 顶级菜单项头部模板(带子菜单) -->
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
<Border
x:Name="Border"
Margin="{StaticResource TopLevelItemMargin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource TopLevelCornerRadius}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 菜单项内容区域 -->
<Grid Margin="{StaticResource TopLevelContentMargin}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 菜单项图标 -->
<ContentPresenter
x:Name="Icon"
Grid.Column="0"
Margin="0,0,6,0"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- 菜单项标题 -->
<ContentPresenter
x:Name="HeaderPresenter"
Grid.Column="1"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Grid>
<!-- 子菜单弹出窗口(使用自定义FluentPopup) -->
<local:FluentPopup
x:Name="PART_Popup"
Grid.Row="1"
Grid.Column="0"
Focusable="False"
HorizontalOffset="-12"
IsOpen="{TemplateBinding IsSubmenuOpen}"
Placement="Bottom"
PlacementTarget="{Binding ElementName=Border}"
PopupAnimation="None"
VerticalOffset="1">
<Grid
x:Name="SubmenuBorder"
Background="{DynamicResource PopupWindowBackground}"
SnapsToDevicePixels="True">
<!-- 子菜单动画变换 -->
<Grid.RenderTransform>
<TranslateTransform />
</Grid.RenderTransform>
<ScrollViewer
Padding="{StaticResource MenuBorderPadding}"
CanContentScroll="True"
Style="{StaticResource MenuItemScrollViewerStyle}">
<Grid>
<!-- 子菜单项呈现器 -->
<ItemsPresenter
x:Name="ItemsPresenter"
Grid.IsSharedSizeScope="True"
KeyboardNavigation.DirectionalNavigation="Cycle"
KeyboardNavigation.TabNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ScrollViewer>
</Grid>
</local:FluentPopup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- 无图标时隐藏图标区域 -->
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed" />
</Trigger>
<!-- 无标题时隐藏标题并移除图标边距 -->
<Trigger Property="Header" Value="{x:Null}">
<Setter TargetName="Icon" Property="Margin" Value="0" />
<Setter TargetName="HeaderPresenter" Property="Visibility" Value="Collapsed" />
</Trigger>
<!-- 鼠标悬停高亮效果 -->
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource MaskColor}" />
</Trigger>
<!-- 子菜单打开时的动画 -->
<Trigger Property="IsSubmenuOpen" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="SubmenuBorder"
Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.Y)"
From="-45"
To="0"
Duration="{StaticResource MenuAnimationDuration}">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- 顶级菜单项模板(无子菜单) -->
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelItemTemplateKey}" TargetType="{x:Type MenuItem}">
<Border
x:Name="Border"
Margin="{StaticResource TopLevelItemMargin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource TopLevelCornerRadius}">
<Grid Margin="{StaticResource TopLevelContentMargin}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 图标区域 -->
<ContentPresenter
x:Name="Icon"
Grid.Column="0"
Margin="0,0,6,0"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- 标题区域 -->
<ContentPresenter
x:Name="HeaderPresenter"
Grid.Column="1"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- 鼠标悬停效果 -->
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource MaskColor}" />
</Trigger>
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="Header" Value="{x:Null}">
<Setter TargetName="Icon" Property="Margin" Value="0" />
<Setter TargetName="HeaderPresenter" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- 子菜单项模板(无子级) -->
<ControlTemplate x:Key="{x:Static MenuItem.SubmenuItemTemplateKey}" TargetType="{x:Type MenuItem}">
<Border
x:Name="Border"
Margin="{StaticResource MenuItemMargin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource MenuCornerRadius}">
<Grid Margin="{StaticResource MenuItemContentPadding}">
<Grid.ColumnDefinitions>
<!-- 图标/复选框列,使用共享大小组确保对齐 -->
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIconCol" />
<!-- 标题内容列 -->
<ColumnDefinition Width="*" />
<!-- 快捷键提示列,使用共享大小组确保对齐 -->
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemRightPartCol" />
</Grid.ColumnDefinitions>
<!-- 复选框图标容器 -->
<Border
x:Name="CheckBoxIconBorder"
Grid.Column="0"
VerticalAlignment="Center"
Visibility="Collapsed">
<Path
x:Name="CheckBoxIcon"
Width="10"
Height="10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Fill="{TemplateBinding Foreground}"
Stretch="Uniform" />
</Border>
<!-- 自定义图标 -->
<ContentPresenter
x:Name="Icon"
Grid.Column="0"
Margin="0,0,6,0"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- 菜单项标题内容 -->
<ContentPresenter
Grid.Column="1"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
ContentSource="Header"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
TextElement.Foreground="{TemplateBinding Foreground}" />
<!-- 快捷键提示文本 -->
<TextBlock
x:Name="InputGestureText"
Grid.Column="2"
Margin="25,0,0,0"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
DockPanel.Dock="Right"
FontSize="11"
Opacity="0.67"
Text="{TemplateBinding InputGestureText}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- 高亮状态(鼠标悬停) -->
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource MaskColor}" />
</Trigger>
<!-- 无自定义图标时隐藏图标区域 -->
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed" />
</Trigger>
<!-- 可复选时显示复选框图标容器 -->
<Trigger Property="IsCheckable" Value="True">
<Setter TargetName="CheckBoxIconBorder" Property="Visibility" Value="Visible" />
</Trigger>
<!-- 已选中时显示复选标记 -->
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckBoxIcon" Property="Data" Value="{StaticResource CheckGraph}" />
</Trigger>
<!-- 无快捷键时隐藏快捷键提示 -->
<Trigger Property="InputGestureText" Value="">
<Setter TargetName="InputGestureText" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- 子菜单头部模板(带下级子菜单) -->
<ControlTemplate x:Key="{x:Static MenuItem.SubmenuHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 菜单项外观 -->
<Border
x:Name="Border"
Grid.Row="1"
Height="{TemplateBinding Height}"
Margin="{StaticResource MenuItemMargin}"
Background="Transparent"
CornerRadius="{StaticResource MenuCornerRadius}">
<Grid x:Name="MenuItemContent" Margin="{StaticResource MenuItemContentPadding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIconCol" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemRightPartCol" />
</Grid.ColumnDefinitions>
<!-- 图标 -->
<ContentPresenter
x:Name="Icon"
Grid.Column="0"
Margin="0,0,6,0"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- 标题 -->
<ContentPresenter
x:Name="HeaderHost"
Grid.Column="1"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
ContentSource="Header"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- 右箭头指示器(表示有子菜单) -->
<Path
Grid.Column="2"
Width="10"
Height="10"
Margin="0,0,4,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Data="{StaticResource ForwardGraph}"
Fill="{TemplateBinding Foreground}"
Opacity="0.67"
Stretch="Uniform" />
</Grid>
</Border>
<!-- 子菜单弹出窗口(向右展开) -->
<local:FluentPopup
x:Name="PART_Popup"
Grid.Row="1"
Focusable="False"
IsOpen="{TemplateBinding IsSubmenuOpen}"
Placement="Right"
PlacementTarget="{Binding ElementName=MenuItemContent}"
PopupAnimation="None">
<Grid x:Name="PopupRoot" Background="{DynamicResource PopupWindowBackground}">
<!-- 子菜单动画变换 -->
<Grid.RenderTransform>
<TranslateTransform />
</Grid.RenderTransform>
<ScrollViewer
Padding="{StaticResource MenuBorderPadding}"
CanContentScroll="True"
Style="{StaticResource MenuItemScrollViewerStyle}">
<!-- 子菜单项容器 -->
<ItemsPresenter
x:Name="ItemsPresenter"
Grid.IsSharedSizeScope="True"
KeyboardNavigation.DirectionalNavigation="Cycle"
KeyboardNavigation.TabNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Grid>
</local:FluentPopup>
</Grid>
<ControlTemplate.Triggers>
<!-- 无图标时优化布局 -->
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed" />
<Setter TargetName="Icon" Property="Margin" Value="0" />
</Trigger>
<!-- 高亮效果 -->
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource MaskColor}" />
</Trigger>
<!-- 子菜单打开动画 -->
<Trigger Property="IsSubmenuOpen" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Y轴平移动画:从-45向下滑入到0 -->
<DoubleAnimation
Storyboard.TargetName="PopupRoot"
Storyboard.TargetProperty="(Grid.RenderTransform).(TranslateTransform.Y)"
From="-45"
To="0"
Duration="{StaticResource MenuAnimationDuration}">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- 默认菜单项样式 -->
<Style x:Key="DefaultMenuItemStyle" TargetType="{x:Type MenuItem}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource DefaultCollectionFocusVisualStyle}" />
<Setter Property="KeyboardNavigation.IsTabStop" Value="True" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Focusable" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Style.Triggers>
<!-- 根据菜单项角色应用不同模板 -->
<!-- 顶级菜单项(带子菜单) -->
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Template" Value="{StaticResource {x:Static MenuItem.TopLevelHeaderTemplateKey}}" />
<Setter Property="Grid.IsSharedSizeScope" Value="True" />
<Setter Property="Height" Value="{x:Static sys:Double.NaN}" />
</Trigger>
<!-- 顶级菜单项(无子菜单) -->
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="Template" Value="{StaticResource {x:Static MenuItem.TopLevelItemTemplateKey}}" />
<Setter Property="Height" Value="{x:Static sys:Double.NaN}" />
</Trigger>
<!-- 子菜单项(带子菜单) -->
<Trigger Property="Role" Value="SubmenuHeader">
<Setter Property="Template" Value="{StaticResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}" />
</Trigger>
<!-- 子菜单项(无子菜单) -->
<Trigger Property="Role" Value="SubmenuItem">
<Setter Property="Template" Value="{StaticResource {x:Static MenuItem.SubmenuItemTemplateKey}}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
三、应用模板和样式到全局
将上面的资源字典合并到应用程序资源中,然后为ContextMenu和MenuItem指定默认样式:
<Style BasedOn="{StaticResource DefaultContextMenuStyle}" TargetType="{x:Type ContextMenu}">
<Style.Setters>
<Setter Property="local:FluentTooltip.UseFluentStyle" Value="True" />
<Setter Property="Background" Value="{DynamicResource PopupWindowBackground}" />
<Setter Property="Foreground" Value="{DynamicResource ForeColor}" />
</Style.Setters>
</Style>
<Style BasedOn="{StaticResource DefaultMenuItemStyle}" TargetType="MenuItem">
<Setter Property="Height" Value="36" />
<Setter Property="Foreground" Value="{DynamicResource ForeColor}" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
注意,ContextMenu需要使用之前文章中的FluentTooltip.UseFluentStyle来实现亚克力材质特效。其内部原理都是反射获取popup的hwnd句柄,然后附加WindowMaterial特效。