WPF:设置当前页面对应MenuItem的背景色(非鼠标悬停)
解决方案
核心问题分析
你直接设置MenuItem.Background无效,是因为自定义的ControlTemplate里没有绑定MenuItem的Background属性,模板内的StackPanel样式完全独立于MenuItem本身的属性。另外,IsFocused属性不适合标记“当前页面对应项”——它仅表示控件是否获得键盘焦点,切换页面时无法稳定保持这个状态。
实现步骤
1. 添加自定义附加属性
创建一个附加属性来标记当前页面对应的MenuItem,替代IsFocused:
Public Class MenuItemExtensions ' 定义附加属性IsActivePage,用于标记是否为当前页面的菜单项 Public Shared ReadOnly IsActivePageProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsActivePage", GetType(Boolean), GetType(MenuItemExtensions), New PropertyMetadata(False)) Public Shared Sub SetIsActivePage(element As MenuItem, value As Boolean) element.SetValue(IsActivePageProperty, value) End Sub Public Shared Function GetIsActivePage(element As MenuItem) As Boolean Return CBool(element.GetValue(IsActivePageProperty)) End Function End Class
2. 修改ControlTemplate,添加活跃状态触发器
在XAML的模板中,通过DataTrigger绑定附加属性,实现活跃状态的背景色切换:
<!-- 注意添加命名空间,将local替换为你的项目命名空间 --> <StackPanel Name="spMainNav" xmlns:local="clr-namespace:YourProjectNamespace"> <StackPanel.Resources> <ControlTemplate x:Key="VsMenuSub" TargetType="MenuItem"> <StackPanel> <Label Content="{TemplateBinding Header}" Foreground="White" /> <StackPanel.Style> <Style TargetType="StackPanel"> <Style.Triggers> <!-- 鼠标悬停样式保持不变 --> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Red" /> </Trigger> <!-- 绑定附加属性,标记当前页面菜单项 --> <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:MenuItemExtensions.IsActivePage)}" Value="True"> <Setter Property="Background" Value="DarkRed" /> </DataTrigger> </Style.Triggers> </Style> </StackPanel.Style> </StackPanel> </ControlTemplate> </StackPanel.Resources> <MenuItem Name="ProjectList" Header="Project List" Template="{StaticResource VsMenuSub}" Click="ProjectList_Click" /> <MenuItem Header="About" Template="{StaticResource VsMenuSub}" Click="About_Click" /> <MenuItem Header="Exit" Template="{StaticResource VsMenuSub}" Click="Exit_Click" /> </StackPanel>
3. 重构后台代码,更新附加属性
修改HighlightMenuItem方法,通过设置附加属性来标记活跃项,同时重置其他项的状态:
Public Shared Sub HighlightMenuItem(sender As MenuItem) Dim sp As StackPanel = sender.Parent ' 重置所有菜单项的活跃状态和标题 For Each child In sp.Children If TypeOf child Is MenuItem Then Dim mi As MenuItem = CType(child, MenuItem) MenuItemExtensions.SetIsActivePage(mi, False) ' 恢复原始标题(移除之前添加的**) If mi.Header.ToString().StartsWith("** ") Then mi.Header = mi.Header.ToString().Substring(3) End If End If Next ' 设置当前菜单项为活跃状态 MenuItemExtensions.SetIsActivePage(sender, True) ' 添加标题标记 If Not sender.Header.ToString().StartsWith("** ") Then sender.Header = "** " & sender.Header End If End Sub
原理说明
- 附加属性
IsActivePage专门用于标记“当前页面对应的菜单项”,逻辑更精准,不受焦点状态影响。 - 模板内的
DataTrigger通过RelativeSource绑定到父级MenuItem的附加属性,当属性值为True时自动切换背景色。 - 后台代码负责维护所有菜单项的活跃状态,确保同一时间只有一个项处于活跃状态。
内容的提问来源于stack exchange,提问作者ElectricFrankincense
相关产品推荐
相关产品推荐

