{"id":253,"date":"2011-10-24T03:16:45","date_gmt":"2011-10-23T16:16:45","guid":{"rendered":"http:\/\/blog.quppa.net\/2011\/10\/24\/windows-theme-fonts-redux-sample-code\/"},"modified":"2011-10-24T03:16:45","modified_gmt":"2011-10-23T16:16:45","slug":"windows-theme-fonts-redux-sample-code","status":"publish","type":"post","link":"https:\/\/www.quppa.net\/blog\/2011\/10\/24\/windows-theme-fonts-redux-sample-code\/","title":{"rendered":"Windows Theme Fonts Redux &#038; Sample Code"},"content":{"rendered":"<blockquote><p><a href=\"https:\/\/github.com\/Quppa\/WindowsThemeFonts\" title=\"GitHub: WindowsThemeFonts\">View source on GitHub.<\/a><\/p><\/blockquote>\n<p>In a <a title=\"Quppa&#39;s Blog: Windows Theme Fonts\" href=\"https:\/\/www.quppa.net\/blog\/2011\/04\/30\/windows-theme-fonts\/\">post earlier this year<\/a>, I investigated how to retrieve information about theme fonts in Windows. Briefly, the <a title=\"MSDN: Visual Styles Reference\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb773178(v=vs.85).aspx\">Visual Styles APIs<\/a> can be used when visual styles are enabled, but values need to be hard-coded (to some extent) otherwise.<\/p>\n<p>Andrew Powell <a title=\"Quppa&#39;s Blog: Comment by Andrew Powell on Windows Theme Fonts\" href=\"https:\/\/www.quppa.net\/blog\/2011\/04\/30\/windows-theme-fonts\/#comments\">commented<\/a> on my previous post noting difficulties in implementing the <a title=\"MSDN: GetThemeFont Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb759745(v=vs.85).aspx\">GetThemeFont function<\/a> in managed code. In this post, I\u2019ll demonstrate how to implement the relevant functions in a simple WPF project. In particular, I\u2019ll focus on displaying information about the \u2018main instruction\u2019 text style as seen in <a title=\"MSDN: Task Dialog\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/bb787471(v=vs.85).aspx\">Task Dialogs<\/a>.<\/p>\n<p>Read on for details.<\/p>\n<p><!--more--><\/p>\n<h1>Part 1: Fallback Values<\/h1>\n<p>As noted last time, the Visual Styles APIs don\u2019t work when visual styles are disabled.<\/p>\n<p>The first step is to call the <a title=\"MSDN: OpenThemeData function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/bb759821(v=vs.85).aspx\">OpenThemeData function<\/a>, which takes a window handle and a class name and returns a handle to theme data. If no match to the specified class name is found, NULL is returned. The class we will reference is TEXTSTYLE, which includes the TEXT_MAININSTRUCTION part. The parts and states for standard controls can be found <a title=\"MSDN: Parts and States\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb773210(VS.85).aspx\">here<\/a>.<\/p>\n<p>We need to use PInvoke to call OpenThemeData (and the associated CloseThemeData). As usual, <a title=\"PInvoke.net\" href=\"http:\/\/www.pinvoke.net\/\">PInvoke.net<\/a> is very useful for getting PInvoke signatures.<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;uxtheme.dll&quot;, ExactSpelling = true, CharSet = CharSet.Unicode)]\npublic static extern IntPtr OpenThemeData(IntPtr hWnd, String classList);<\/pre>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;uxtheme.dll&quot;, ExactSpelling = true)]\npublic extern static Int32 CloseThemeData(IntPtr hTheme);<\/pre>\n<pre class=\"lang:c# decode:true \">IntPtr hTheme = OpenThemeData(IntPtr.Zero, &quot;TEXTSTYLE&quot;);<\/pre>\n<p>If hTheme is NULL (that is, IntPtr.Zero), we can assume that visual styles are disabled (and in the event that this is not the case, we still won&#8217;t be able to use GetThemeFont and GetThemeColor). The fallback values that we should use are defined in AeroStyle.xml, which can be found in the Windows SDK.<\/p>\n<pre class=\"lang:xml decode:true \">&lt;class name=&quot;TextStyle&quot;&gt;\n    &lt;part name=&quot;MainInstruction&quot;&gt;\n        &lt;caption&gt;\n            &lt;TextColor&gt;0 51 153&lt;\/TextColor&gt;\n            &lt;Font&gt;Segoe UI, 12, Quality:ClearType&lt;\/Font&gt;\n            &lt;ClassicValue type=&quot;TextColor&quot;&gt;WindowText&lt;\/ClassicValue&gt;\n            &lt;ClassicValue type=&quot;Font&quot;&gt;CaptionFont&lt;\/ClassicValue&gt;\n        &lt;\/caption&gt;\n    &lt;\/part&gt;\n    ...\n&lt;\/class&gt;<\/pre>\n<p>Fortunately, the <a title=\"MSDN: SystemFonts Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.systemfonts.aspx\">SystemFonts<\/a> and <a title=\"MSDN: SystemColors Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.systemcolors.aspx\">SystemColors<\/a> classes in the System.Windows namespace allow easy access to these values:<\/p>\n<pre class=\"lang:c# decode:true \">FontFamily fontfamily;\ndouble fontsize;\nFontWeight fontweight;\nBrush foreground;\nif (hTheme == IntPtr.Zero) {\n  fontfamily = SystemFonts.CaptionFontFamily;\n  fontsize = SystemFonts.CaptionFontSize;\n  fontweight = SystemFonts.CaptionFontWeight;\n  foreground = SystemColors.WindowTextBrush;\n} else {\n  ...\n}<\/pre>\n<h1>Part 2: GetThemeFont and GetThemeColor<\/h1>\n<p>In the case where visual styles <em>are <\/em>enabled, hTheme will hold a handle to theme data that we will pass to the GetThemeFont and GetThemeColor functions.<\/p>\n<p>Firstly, the PInvoke signatures:<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;uxtheme&quot;, ExactSpelling = true, CharSet = CharSet.Unicode)]\npublic extern static Int32 GetThemeFont(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, int iPropId, out LOGFONT pFont);<\/pre>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;uxtheme&quot;, ExactSpelling = true)]\npublic extern static Int32 GetThemeColor(IntPtr hTheme, int iPartId, int iStateId, int iPropId, out COLORREF pColor);<\/pre>\n<pre class=\"lang:c# decode:true \">[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]\npublic struct LOGFONT\n{\n  public const int LF_FACESIZE = 32;\n  public int lfHeight;\n  public int lfWidth;\n  public int lfEscapement;\n  public int lfOrientation;\n  public int lfWeight;\n  public byte lfItalic;\n  public byte lfUnderline;\n  public byte lfStrikeOut;\n  public byte lfCharSet;\n  public byte lfOutPrecision;\n  public byte lfClipPrecision;\n  public byte lfQuality;\n  public byte lfPitchAndFamily;\n  [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]\n  public string lfFaceName;\n}<\/pre>\n<pre class=\"lang:c# decode:true \">[StructLayout(LayoutKind.Sequential)]\npublic struct COLORREF\n{\n  public byte R;\n  public byte G;\n  public byte B;\n}<\/pre>\n<p>Let\u2019s start with GetThemeColor:<\/p>\n<pre class=\"lang:c# decode:true \">\/\/ from vsstyle.h\nint TEXT_MAININSTRUCTION = 1;\n\/\/ from vssym32.h\nint TMT_TEXTCOLOR = 3803;\nCOLORREF pColor;\nGetThemeColor(hTheme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, out pColor);\nforeground = new SolidColorBrush(Color.FromRgb(pColor.R, pColor.G, pColor.B));<\/pre>\n<p>Now for GetThemeFont:<\/p>\n<pre class=\"lang:c# decode:true \">\/\/ from vssym32.h\nint TMT_FONT = 210;\n<\/pre>\n<pre class=\"lang:c# decode:true \">LOGFONT pFont;\nGetThemeFont(hTheme, IntPtr.Zero, TEXT_MAININSTRUCTION, 0, TMT_FONT, out pFont);\nfontfamily = new FontFamily(pFont.lfFaceName);\nfontsize = Math.Abs(pFont.lfHeight);\nfontweight = FontWeight.FromOpenTypeWeight(pFont.lfWeight);<\/pre>\n<p>At this point, we\u2019re finished, though we need to call CloseThemeData, preferably in a <a title=\"MSDN: try-finally\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/zwc8s4fz(v=VS.100).aspx\">finally<\/a> block.<\/p>\n<p><strong>Update (2011-10-25): <\/strong>Something I missed in the original implementation was that the lfHeight value represents the font height in pixels, which is a problem as WPF uses device-independent units (DIUs) for its measurements (something I\u2019ve <a title=\"Quppa&#39;s Blog: Pixel Measurements in WPF\" href=\"https:\/\/www.quppa.net\/blog\/2011\/01\/07\/pixel-measurements-in-wpf\/\">written about before<\/a>). To properly support non-default DPI settings, the value in pixels should be converted to DIUs. I\u2019ve added code to the sample project below that does this.<\/p>\n<h1>Download Sample<\/h1>\n<p>You can download a working sample here (<strong>updated 2011-10-25<\/strong>):<\/p>\n<blockquote>\n<p><a title=\"Windows Theme Fonts Sample Project\" href=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/WindowsThemeFontsSample.7z\">WindowsThemeFontsSample.7z<\/a><\/p>\n<p>14,489 bytes; SHA-1: B01722E889780D94CDB26E31F115F4B2DF2E7111<\/p>\n<\/blockquote>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px auto; border: 0px currentcolor; float: none; display: block; background-image: none;\" title=\"WPF Windows Theme Fonts Sample: Aero\" border=\"0\" alt=\"WPF Windows Theme Fonts Sample: Aero\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/WPFSampleThemed.png\" width=\"298\" height=\"201\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px auto; border: 0px currentcolor; float: none; display: block; background-image: none;\" title=\"Task Dialog: Aero\" border=\"0\" alt=\"Task Dialog: Aero\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/TaskDialogThemed.png\" width=\"396\" height=\"171\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px auto; border: 0px currentcolor; float: none; display: block; background-image: none;\" title=\"WPF Windows Theme Fonts Sample: Windows Classic\" border=\"0\" alt=\"WPF Windows Theme Fonts Sample: Windows Classic\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/WPFSampleClassic.png\" width=\"254\" height=\"173\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px auto; border: 0px currentcolor; float: none; display: block; background-image: none;\" title=\"Task Dialog: Windows Classic\" border=\"0\" alt=\"Task Dialog: Windows Classic\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/TaskDialogClassic1.png\" width=\"396\" height=\"160\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px auto; border: 0px currentcolor; float: none; display: block; background-image: none;\" title=\"WPF Windows Theme Fonts Sample: Windows Classic (Custom)\" border=\"0\" alt=\"WPF Windows Theme Fonts Sample: Windows Classic (Custom)\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/WPFSampleClassicCustom.png\" width=\"244\" height=\"174\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px auto; border: 0px currentcolor; float: none; display: block; background-image: none;\" title=\"Task Dialog: Windows Classic (Custom)\" border=\"0\" alt=\"Task Dialog: Windows Classic (Custom)\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/TaskDialogClassicCustom.png\" width=\"396\" height=\"160\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>View source on GitHub. In a post earlier this year, I investigated how to retrieve information about theme fonts in Windows. Briefly, the Visual Styles APIs can be used when visual styles are enabled, but values need to be hard-coded (to some extent) otherwise. Andrew Powell commented on my previous post noting difficulties in implementing &hellip; <a href=\"https:\/\/www.quppa.net\/blog\/2011\/10\/24\/windows-theme-fonts-redux-sample-code\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Windows Theme Fonts Redux &#038; Sample Code&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,9],"tags":[56,148,164,165,166,170,185],"class_list":["post-253","post","type-post","status-publish","format-standard","hentry","category-programming","category-windows","tag-fonts","tag-themes","tag-visual-styles","tag-vsstyle","tag-vssym32","tag-win32","tag-wpf"],"_links":{"self":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/253","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/comments?post=253"}],"version-history":[{"count":0,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/253\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/media?parent=253"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/categories?post=253"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/tags?post=253"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}