本文记录一个 WPF 已知问题,在 ContextMenu 的 Resources 里定义 Separator 的默认样式,在 ContextMenu 里面的 Separator 将应用不上,或者说不会找到此默认的样式。需要明确给 Style 一个 Key 且在 Separator 写明此 Key 才能应用上
如下面的例子,在 ContextMenu 的 Resources 资源里面定义了 Separator 的默认样式
< Style TargetType = " Separator " >
< Setter Property = " Margin " Value = " 10,10,10,10 " ></ Setter >
接着在 ContextMenu 里面存放一个 Separator 元素,可以看到此 Separator 元素没有使用或者说找到定义的样式,视觉上就是 Margin 没有生效
< Style TargetType = " Separator " >
< Setter Property = " Margin " Value = " 10,10,10,10 " ></ Setter >
而如果给 Separator 的 Style 加上 Key 且在 Separator 写明了此 Key 那么将可以成功应用上,如下面代码
< Style x : Key = " SeparatorStyle " TargetType = " Separator " >
< Setter Property = " Margin " Value = " 10,10,10,10 " ></ Setter >
< Separator Style = " {StaticResource SeparatorStyle} " ></ Separator >
此问题是在 WPF 代码里面写了特殊判断逻辑,预计是有我没有理解的坑才如此做。感谢 少珺 工具人帮我找到了在 WPF 框架里面的问题
为了方便说明问题,我将给出可以运行的测试代码,此测试代码可以在本文末尾找到项目的下载
新建一个 WPF 项目,编辑主窗口,添加以下代码
< Grid Background = " White " MouseDown = " Grid_OnMouseDown " >
< Style TargetType = " Separator " >
< Setter Property = " Margin " Value = " 10,10,10,10 " ></ Setter >
< Style x : Key = " SeparatorStyle " TargetType = " Separator " >
< Setter Property = " Margin " Value = " 10,10,10,10 " ></ Setter >
< Separator Style = " {StaticResource SeparatorStyle} " ></ Separator >
对应的后台代码如下
private void Grid_OnMouseDown ( object sender , MouseButtonEventArgs e )
grid.ContextMenu.IsOpen = true ;
此时点击窗口内容,即可看到弹出了菜单
弹出的菜单的两条分割线的 Margin 是不相同的
根本原因是在 WPF 里面,对于在 Menu 里面的 Separator 采用的是如下逻辑,以下代码可以从 WPF 官方开源仓库 https://github.com/dotnet/wpf/blob/1aab9e3f42dbf550797bff97a32f2dbfb61a3198/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L1344-L1353 找到。我此问题报告给 WPF 官方,请看 https://github.com/dotnet/wpf/issues/7268
Separator separator = item as Separator ;
BaseValueSourceInternal vs = separator. GetValueSource (StyleProperty, null , out hasModifiers);
if (vs <= BaseValueSourceInternal.ImplicitReference)
separator. SetResourceReference (StyleProperty, SeparatorStyleKey);
separator.DefaultStyleKey = SeparatorStyleKey;
从上面代码可以看到,判断如果样式是小于等于 ImplicitReference 优先级的,那就采用默认的 SeparatorStyleKey 作为样式属性。如果没有在代码里面明确给定资源的 Key 内容,那以上代码的 vs 就是 ImplicitReference 优先级,于是样式就被修改为默认的主题样式
这是在 WPF 里面特别给定的代码,也许是大佬们为了修复某个我理解不了的坑
本文的代码放在github 和 gitee 欢迎访问
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin b820847a1af20370de28a1e73e32df9561a98ecc
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin b820847a1af20370de28a1e73e32df9561a98ecc
获取代码之后,进入 HayhachujedaKikunayreefee 文件夹
更多 WPF 已知问题请参阅 博客导航
大佬 czdietrich 告诉我说,这是特意的。原因是 Separator 在很多个地方都会使用,如果在 Menu 里,也采用默认继承的关系,将会让 Separator 的样式打架。毕竟在 Menu 里面的,是期望比较特殊一些的。于是就限制了,如果是放在 Menu 里面,应该采用的是 MenuItem.SeparatorStyleKey
样式的才会作为默认的继承样式,如下面代码
< Window x : Class = " TestContextMenuSeparatorStyle.MainWindow "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml " >
< Style x : Key = " {x:Static MenuItem.SeparatorStyleKey} " TargetType = " Separator " >
< Setter Property = " Background " Value = " Tomato " />
< Grid Background = " White " >
< MenuItem Header = " Item 1 " />
< MenuItem Header = " Item 2 " />
以上的代码也符合 WPF 的逻辑,详细请看 https://github.com/dotnet/wpf/issues/7268#issuecomment-1315303718
原文链接: http://blog.lindexi.com/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-Separator-%E6%97%A0%E6%B3%95%E5%BA%94%E7%94%A8-ContextMenu-%E5%AE%9A%E4%B9%89%E7%9A%84%E9%BB%98%E8%AE%A4%E6%A0%B7%E5%BC%8F
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系 。