介绍我查看WPF控件模板的常用方法。包括创建控件并显示查看、反编译查看BAML资源和查看源代码。
背景在使用WPF的过程中,通常需要定制控制器的外观。此时,有必要查看其原始样式或模板以供参考。这可以减少许多工作,只修改所需的部分,并避免在修改模板或样式后失去某些功能。以下是我常用的方法。
创建控件并显示检查模板的原理您可以手动创建控件,然后使用XamlWriter保存模板来查看控件的当前模板。如果您对此感兴趣,请查看WPF编程宝典——C#2010版》(《Pro WPF in C# 2010》)17.2.2 分析控件的相关部分。
简单修改后,上述方法也可用于查看风格或第三方控件的模板。例如,我用这种方法查看了Devexpress中图表控件的模板。
不足- XAML代码保存在XamlWriter类中有些冗余和不直观,因为它总是使用属性元素语法而不是特性语法。
- 对于某些类型,无法正确检查。例如,缺乏默认构造函数的控件(在第三方控件中更常见,如Devexpress)。另一个例子是一些不能直接显示在窗口中的类型(Tolltip、contextmenu等)
基于WPF编程宝典的基础,我自己改进了一个控件检查工具。具有以下功能:
- 可查看控件模板和样式。
- 支持检查第三方控件
- 支持语法亮点
效果如下:
在实际显示控件之前,模板是空的。因此,在代码中使用了一个小技巧,将控件放入一个1像素宽高的窗口中,以便检查控件模板。
博客园:
WPFTemplateViewer
反编译查看BAML资源原理一般来说,控件提供商在资源字典中为控件准备了样式和模板。资源字典存储在控件所在的程序集或外部程序集中。必要时,可以使用C#反编译工具(如ILSpy)、dotPeek)查看BAML资源。
主题在WPF3.0中,WPF为自己的控件准备了4套主题,即Aero、Classic、Luna、Royale。主题资源字典文件名及相应含义如下:
Aero2和Aerolite在WPF4.0中又增加了两套主题。在Win8及以上版本的系统中,Framework会自动将Aero主题转换为Aero2主题,可以通过反编译PresentationFramework来证实。Aerolite没有查询权威声明,网上有人说是用于Windows的 Server 2012。
对上述代码进行整理,以确定当前使用的主题名称和颜色 。
public static Tuple<string, string> GetThemeInfo() { StringBuilder themePathBuff = new StringBuilder(260); StringBuilder themeColorBuff = new StringBuilder(260); if (GetCurrentThemeName(themePathBuff, themePathBuff.Capacity, themeColorBuff, themeColorBuff.Capacity, null, 0) == 0) { string themeName = System.IO.Path.GetFileNameWithoutExtension(themePathBuff.ToString()); string themeColor = themeColorBuff.ToString(); if (string.Compare(themeName, "aero", StringComparison.OrdinalIgnoreCase) == 0 && Environment.OSVersion.Version >= new Version(6, 2)) { themeName = “Aero2”; } return new Tuple<string, string>(themeName, themeColor); } else { return new Tuple<string, string>(string.Empty, string.Empty); } } [DllImport("uxtheme.dll", CharSet = CharSet.Unicode, EntryPoint = "GetCurrentThemeName")] private static extern int GetCurrentThemeName(StringBuilder pszThemeFileName, int dwMaxNameChars, StringBuilder pszColorBuff, int cchMaxColorChars, StringBuilder pszSizeBuff, int cchMaxSizeChars);
查找控件样式顺序Framework在寻找控件样式时,首先要尝试寻找具体主题名的资源,如果找不到,那就去寻找Classic.xaml。最后,寻找常规资源(generic.xaml)。
控件样式位于定义程序集还是外部程序集,可以通过反编译查看控件定义程序集元数据的themeinfoattribute特性来确定。themeinfoatribute结构函数包括两个参数,前者指定特定主题资源的位置,后者指定常规资源的位置。
外部程序集WPF自带的控件通过ILSpy反编译查看Presentationframework程序集元数据,发现Themeinfratibute特性信息如下:
ThemeInfo(ResourceDictionaryLocation.ExternalAssembly, ResourceDictionaryLocation.None)
表明控件没有常规资源,控件的主题集中在外部程序上。
每个主题对应的DLL名称是PresentationFramework.主题名称,如Aero对应Presentationframework.Aero.dll、Clasic对应Presentationframework.Classic.dll。
定义程序集例如,常用的Devexpress,通过ILSpy反编译查看Devexpress.Xpf.Core。
表明控件没有特定的主题资源,只有常规资源,位于当前程序集中。
反编译查看BAML资源以ILSpy为例,说明如何查看BAML资源。
- 用ILSpy打开程序集
- 展开程序集
- 展开程序集资源
- 展开(程序集名称).g.resources
- 根据上述命名规则,确定BAML资源文件。XAML,无论是特定的主题资源还是常规资源,都位于Themes文件夹下。
- 在BAML资源文件中找到控制样式。值得注意的是,资源字典可以嵌套在包含资源字典中,可能需要仔细分析和多次搜索来定位所需的控制样式。
下图为PresentationFramework特定主题BAML
下图为Devexpresss.Xpf.Core.常规资源BAMLv15.2,可见嵌套包含其他资源字典。
不足在某些情况下,它将更加复杂,需要仔细分析。PresentationFramework中的控件仍然很容易找到,我通常通过x:Type (控件名称) 直接查找。
查看源码原理这个标题只是为了整洁。有代码,还说什么,不仅知道为什么,还知道为什么。
不足该方法仅适用于开源控件。如Extendeded。 WPF Toolkit。
好消息是微软也开源了WPF,风格相关代码位于Microsoft.DotNet.Wpf/src/Themes/XAML目录。相同控件的所有主题样式都在相应控件的XMAL文件中。
参考链接控件创建概述What is the theme name for a WPF application on Windows 8?