{"id":88,"date":"2018-10-20T23:15:04","date_gmt":"2018-10-20T23:15:04","guid":{"rendered":"https:\/\/engy.us\/blog\/?p=88"},"modified":"2020-01-01T05:54:33","modified_gmt":"2020-01-01T05:54:33","slug":"dark-theme-in-wpf","status":"publish","type":"post","link":"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/","title":{"rendered":"Dark Theme in WPF"},"content":{"rendered":"<p>In a recent Windows 10 update a toggle switch was added to allow the user to specify that they wanted &#8220;Dark&#8221; themes in apps:<\/p>\n<p><img data-attachment-id=\"89\" data-permalink=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/windowsdarkmode\/\" data-orig-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/WindowsDarkMode.png?fit=655%2C437&amp;ssl=1\" data-orig-size=\"655,437\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"WindowsDarkMode\" data-image-description=\"\" data-medium-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/WindowsDarkMode.png?fit=300%2C200&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/WindowsDarkMode.png?fit=525%2C350&amp;ssl=1\" loading=\"lazy\" class=\"alignnone size-full wp-image-89\" src=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/WindowsDarkMode.png?resize=525%2C350&#038;ssl=1\" alt=\"\" width=\"525\" height=\"350\" srcset=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/WindowsDarkMode.png?w=655&amp;ssl=1 655w, https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/WindowsDarkMode.png?resize=300%2C200&amp;ssl=1 300w\" sizes=\"(max-width: 525px) 100vw, 525px\" data-recalc-dims=\"1\" \/><\/p>\n<p>I decided to add support for this to <a href=\"http:\/\/vidcoder.net\/\">VidCoder<\/a>. But no updates to WPF were made to make this easy. WPF does having theming support where it can pull in different .xaml resource files from a Themes folder, but this is all Luna\/Aero\/Win10 styling that is automatically applied based on your OS. After digging in the WPF source code I determined that there&#8217;s no way to hook in your own theme to this or manually alter which theme is loaded.<\/p>\n<p>Furthermore, the actual dark theme setting is not exposed in a friendly manner to WPF. But we can still do it. Let&#8217;s get started.<\/p>\n<h2>Detecting Windows Dark Mode setting + High Contrast<\/h2>\n<p>The first step is finding out what theme we should be applying. To do that we need to tell what dark mode choice the user has made and detect when it&#8217;s changed. We also need to tell if the user has turned on High Contrast and detect when it&#8217;s changed.<\/p>\n<h3>Dark mode detection<\/h3>\n<p>To get the windows theme we need to poke into the registry. I used a WMI query to watch the registry for changes so we can update the app theme when they change the setting.<\/p>\n<pre>private const string RegistryKeyPath = @\"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\";\n\nprivate const string RegistryValueName = \"AppsUseLightTheme\";\n\nprivate enum WindowsTheme\n{\n\tLight,\n\tDark\n}\n\npublic void WatchTheme()\n{\n\tvar currentUser = WindowsIdentity.GetCurrent();\n\tstring query = string.Format(\n\t\tCultureInfo.InvariantCulture,\n\t\t@\"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\\\{1}' AND ValueName = '{2}'\",\n\t\tcurrentUser.User.Value,\n\t\tRegistryKeyPath.Replace(@\"\\\", @\"\\\\\"),\n\t\tRegistryValueName);\n\n\ttry\n\t{\n\t\tvar watcher = new ManagementEventWatcher(query);\n\t\twatcher.EventArrived += (sender, args) =&gt;\n\t\t{\n\t\t\tWindowsTheme newWindowsTheme = GetWindowsTheme();\n\t\t\t\/\/ React to new theme\n\t\t};\n\n\t\t\/\/ Start listening for events\n\t\twatcher.Start();\n\t}\n\tcatch (Exception)\n\t{\n\t\t\/\/ This can fail on Windows 7\n\t}\n\n\tWindowsTheme initialTheme = GetWindowsTheme();\n}\n\nprivate static WindowsTheme GetWindowsTheme()\n{\n\tusing (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath))\n\t{\n\t\tobject registryValueObject = key?.GetValue(RegistryValueName);\n\t\tif (registryValueObject == null)\n\t\t{\n\t\t\treturn WindowsTheme.Light;\n\t\t}\n\n\t\tint registryValue = (int)registryValueObject;\n\n\t\treturn registryValue &gt; 0 ? WindowsTheme.Light : WindowsTheme.Dark;\n\t}\n}\n<\/pre>\n<h3>High Contrast detection<\/h3>\n<p>Our app will have 3 &#8220;modes&#8221;: Light, Dark and High Contrast. In High Contrast we&#8217;ll reference a limited set of system colors which will come from the user&#8217;s settings:<\/p>\n<p><img data-attachment-id=\"90\" data-permalink=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/highcontrastwindows\/\" data-orig-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/HighContrastWindows.png?fit=489%2C620&amp;ssl=1\" data-orig-size=\"489,620\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"HighContrastWindows\" data-image-description=\"\" data-medium-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/HighContrastWindows.png?fit=237%2C300&amp;ssl=1\" data-large-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/HighContrastWindows.png?fit=489%2C620&amp;ssl=1\" loading=\"lazy\" class=\"alignnone size-full wp-image-90\" src=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/HighContrastWindows.png?resize=489%2C620&#038;ssl=1\" alt=\"\" width=\"489\" height=\"620\" srcset=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/HighContrastWindows.png?w=489&amp;ssl=1 489w, https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/HighContrastWindows.png?resize=237%2C300&amp;ssl=1 237w\" sizes=\"(max-width: 489px) 100vw, 489px\" data-recalc-dims=\"1\" \/><\/p>\n<p>We&#8217;ll need to find if High Contrast is enabled and when it changes:<\/p>\n<pre>bool isHighContrast = SystemParameters.HighContrast;\nSystemParameters.StaticPropertyChanged += (sender, args) =&gt;\n{\n\tif (args.PropertyName == nameof(SystemParameters.HighContrast))\n\t{\n\t\tbool newIsHighContrast = SystemParameters.HighContrast;\n\t}\n};\n<\/pre>\n<h3>Combining the values<\/h3>\n<p>We want to end up with a value from this enum:<\/p>\n<pre>public enum AppTheme\n{\n\tLight,\n\tDark,\n\tHighContrast\n}\n<\/pre>\n<p>You&#8217;ll need to use logic to combine light\/dark with the High Contrast bool to get the theme and react to changes. I used ReactiveX Observables but you might have something else. Anyhow, if High Contrast is enabled, use that theme. Otherwise, use the Light\/Dark as indicated by the registry key setting.<\/p>\n<h2>Setting up theme swapping<\/h2>\n<p>After we know what theme we want, we now need to apply it. Fortunately WPF has an excellent mechanism to swap out styles: ResourceDictionaries. Make your dictionaries like so:<\/p>\n<p><img data-attachment-id=\"91\" data-permalink=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/themesfolder\/\" data-orig-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/ThemesFolder.png?fit=178%2C77&amp;ssl=1\" data-orig-size=\"178,77\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"ThemesFolder\" data-image-description=\"\" data-medium-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/ThemesFolder.png?fit=178%2C77&amp;ssl=1\" data-large-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/ThemesFolder.png?fit=178%2C77&amp;ssl=1\" loading=\"lazy\" class=\"alignnone size-full wp-image-91\" src=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/ThemesFolder.png?resize=178%2C77&#038;ssl=1\" alt=\"\" width=\"178\" height=\"77\" data-recalc-dims=\"1\" \/><\/p>\n<p>Then inside each:<\/p>\n<pre>&lt;ResourceDictionary\n\txmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n\txmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"&gt;\n\t&lt;SolidColorBrush x:Key=\"MyBackgroundBrush\" Color=\"#8EC2FA\" \/&gt;\n&lt;\/ResourceDictionary&gt;\n<\/pre>\n<p>We can specify the &#8220;default&#8221; style by including Light.xaml in App.xaml:<\/p>\n<pre>&lt;Application.Resources&gt;\n\t&lt;ResourceDictionary&gt;\n\t\t&lt;ResourceDictionary.MergedDictionaries&gt;\n\t\t\t&lt;ResourceDictionary Source=\"\/Themes\/Light.xaml\" \/&gt;\n\t\t&lt;\/ResourceDictionary.MergedDictionaries&gt;\n\n\t\t&lt;!-- Other App-level items --&gt;\n<\/pre>\n<p>That will make the designer happy. Though obviously we don&#8217;t want to have that theme all the time. We can swap out the theme before opening any windows in App.xaml.cs:<\/p>\n<pre>this.Resources.MergedDictionaries[0].Source =\n    new Uri($\"\/Themes\/{appTheme}.xaml\", UriKind.Relative);<\/pre>\n<p>That way the app loads up with the correct theme. You can also run this code when the user updates the theme.<\/p>\n<h2>Populating the theme dictionaries<\/h2>\n<p>The only thing you want to put into the theme dictionaries are Brush resources (and maybe Colors if you need them). Dark and Light should have whatever looks good to you. For High Contrast, we should pick from the official <a href=\"https:\/\/blogs.msdn.microsoft.com\/wpf\/2010\/11\/30\/systemcolors-reference\/\">SystemColors<\/a>. For example, if you had a special window background brush, you&#8217;d want to define it as this in HighContrast.xaml:<\/p>\n<pre>&lt;SolidColorBrush x:Key=\"MyBackgroundBrush\" Color=\"{x:Static SystemColors.WindowColor}\" \/&gt;\n<\/pre>\n<p>If you choose from SystemColors, it will work for any High Contrast variant the user chooses, even if they customize it.<\/p>\n<p>Another trick you can use in Dark.xaml is overriding system colors with your own:<\/p>\n<pre>&lt;SolidColorBrush x:Key=\"{x:Static SystemColors.WindowBrushKey}\" Color=\"Black\" \/&gt;\n&lt;SolidColorBrush x:Key=\"{x:Static SystemColors.WindowTextBrushKey}\" Color=\"White\" \/&gt;\n<\/pre>\n<p>This causes any standard control that uses the system colors to use the one you&#8217;ve supplied.<\/p>\n<h2>Referencing theme colors<\/h2>\n<p>Now to use a theme color you should always refer to it like so:<\/p>\n<pre>{DynamicResource MyBackgroundBrush}<\/pre>\n<p>A StaticResource never changes, but a DynamicResource reference can update, which comes in handy when the users switches on\/off dark mode or High Contrast.<\/p>\n<h2>Theming built-in controls<\/h2>\n<p>While the SystemColor overrides I mentioned earlier can help a lot with re-theming built-in controls, unfortunately there are a lot of controls that don&#8217;t pay any attention to system colors.<\/p>\n<p>Button, for example is one of them. Unfortunately, there doesn&#8217;t appear to be any way to override these colors. The only way to re-skin them is to make a copy of their ControlTemplate and insert references to our own themed colors. Unfortunately that means that the controls will now look the same no matter what version of Windows the user has, but I don&#8217;t know of any way around it. I based mine off the styling in Windows 10, just so it would look consistent in the latest version and stay &#8220;current&#8221; longer.<\/p>\n<p>Anyway, there&#8217;s a couple ways to do it: One by copying the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/wpf\/controls\/button-styles-and-templates\">Styles and Templates documentation<\/a> for the control you want to theme. Another is by right clicking on the control in the designer, then Edit Template -&gt; Edit a Copy.<\/p>\n<p>You&#8217;ll want to park the style with ControlTemplate override in App.xaml (Or another dictionary referenced by App.xaml):<\/p>\n<pre>&lt;Style x:Key=\"ButtonBaseStyle\" TargetType=\"Button\"&gt;\n\t&lt;Setter Property=\"Background\" Value=\"{DynamicResource Button.Static.Background}\" \/&gt;\n\t&lt;Setter Property=\"BorderBrush\" Value=\"{DynamicResource Button.Static.Border}\" \/&gt;\n\t&lt;Setter Property=\"Foreground\" Value=\"{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}\" \/&gt;\n\t&lt;Setter Property=\"BorderThickness\" Value=\"1\" \/&gt;\n\t&lt;Setter Property=\"HorizontalContentAlignment\" Value=\"Center\" \/&gt;\n\t&lt;Setter Property=\"VerticalContentAlignment\" Value=\"Center\" \/&gt;\n\t&lt;Setter Property=\"Padding\" Value=\"1\" \/&gt;\n\t&lt;Setter Property=\"Template\"&gt;\n\t\t&lt;Setter.Value&gt;\n\t\t\t&lt;ControlTemplate TargetType=\"{x:Type Button}\"&gt;\n\t\t\t\t&lt;Border\n\t\t\t\t\tx:Name=\"border\"\n\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\n\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\n\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\n\t\t\t\t\tSnapsToDevicePixels=\"true\"&gt;\n\t\t\t\t\t&lt;ContentPresenter\n\t\t\t\t\t\tx:Name=\"contentPresenter\"\n\t\t\t\t\t\tMargin=\"{TemplateBinding Padding}\"\n\t\t\t\t\t\tHorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n\t\t\t\t\t\tVerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n\t\t\t\t\t\tFocusable=\"False\"\n\t\t\t\t\t\tRecognizesAccessKey=\"True\"\n\t\t\t\t\t\tSnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" \/&gt;\n\t\t\t\t&lt;\/Border&gt;\n\t\t\t\t&lt;ControlTemplate.Triggers&gt;\n\t\t\t\t\t&lt;Trigger Property=\"IsDefaulted\" Value=\"true\"&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"BorderBrush\" Value=\"{DynamicResource {x:Static SystemColors.HighlightBrushKey}}\" \/&gt;\n\t\t\t\t\t&lt;\/Trigger&gt;\n\t\t\t\t\t&lt;Trigger Property=\"IsMouseOver\" Value=\"true\"&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"Background\" Value=\"{DynamicResource Button.MouseOver.Background}\" \/&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"BorderBrush\" Value=\"{DynamicResource Button.MouseOver.Border}\" \/&gt;\n\t\t\t\t\t&lt;\/Trigger&gt;\n\t\t\t\t\t&lt;Trigger Property=\"IsPressed\" Value=\"true\"&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"Background\" Value=\"{DynamicResource Button.Pressed.Background}\" \/&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"BorderBrush\" Value=\"{DynamicResource Button.Pressed.Border}\" \/&gt;\n\t\t\t\t\t&lt;\/Trigger&gt;\n\t\t\t\t\t&lt;Trigger Property=\"IsEnabled\" Value=\"false\"&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"Background\" Value=\"{DynamicResource Button.Disabled.Background}\" \/&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"border\" Property=\"BorderBrush\" Value=\"{DynamicResource Button.Disabled.Border}\" \/&gt;\n\t\t\t\t\t\t&lt;Setter TargetName=\"contentPresenter\" Property=\"TextElement.Foreground\" Value=\"{DynamicResource Button.Disabled.Foreground}\" \/&gt;\n\t\t\t\t\t&lt;\/Trigger&gt;\n\t\t\t\t&lt;\/ControlTemplate.Triggers&gt;\n\t\t\t&lt;\/ControlTemplate&gt;\n\t\t&lt;\/Setter.Value&gt;\n\t&lt;\/Setter&gt;\n&lt;\/Style&gt;\n&lt;Style BasedOn=\"{StaticResource ButtonBaseStyle}\" TargetType=\"Button\" \/&gt;\n<\/pre>\n<p>I ripped out the FocusVisualStyle override since the default one seems to work fine for all themes. I also include both a &#8220;Base&#8221; style and an implicit style (that has no key). Controls that don&#8217;t specify a style will pick up the implicit style, while control that do need to specify a style can base it on the Base style.<\/p>\n<p>I also moved all the &#8220;Brush&#8221; resources out to the Theme dictionaries where they belong, and changed all the references to {DynamicResource}.<\/p>\n<p>One thing to watch out for is that some overrides like ComboBox will reference a control from PresentationFramework.Aero2 in their ControlTemplate. If you see a namespace brought in that references Aero2, that means your program won&#8217;t work on Windows 7. To keep it compatible, delete the &#8220;2&#8221; to reference PresentationFramework.Aero.<\/p>\n<p>There&#8217;s a lot more &#8220;grunt work&#8221; involved in picking all the colors, but that covers the overall strategy I used to convert all the app UI. Here&#8217;s our new, themed UI:<\/p>\n<p><img data-attachment-id=\"94\" data-permalink=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/vidcoderdarkexample\/\" data-orig-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/VidCoderDarkExample.png?fit=426%2C219&amp;ssl=1\" data-orig-size=\"426,219\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"VidCoderDarkExample\" data-image-description=\"\" data-medium-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/VidCoderDarkExample.png?fit=300%2C154&amp;ssl=1\" data-large-file=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/VidCoderDarkExample.png?fit=426%2C219&amp;ssl=1\" loading=\"lazy\" class=\"alignnone size-full wp-image-94\" src=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/VidCoderDarkExample.png?resize=426%2C219&#038;ssl=1\" alt=\"\" width=\"426\" height=\"219\" srcset=\"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/VidCoderDarkExample.png?w=426&amp;ssl=1 426w, https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/VidCoderDarkExample.png?resize=300%2C154&amp;ssl=1 300w\" sizes=\"(max-width: 426px) 100vw, 426px\" data-recalc-dims=\"1\" \/><\/p>\n<h2>Window title bar<\/h2>\n<p>Now that we&#8217;ve converted all of our UI, we might see something like this:<\/p>\n<p><img data-attachment-id=\"118\" data-permalink=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/pretitlebartheme\/\" data-orig-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/PreTitleBarTheme.png?fit=293%2C186&amp;ssl=1\" data-orig-size=\"293,186\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"PreTitleBarTheme\" data-image-description=\"\" data-medium-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/PreTitleBarTheme.png?fit=293%2C186&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/PreTitleBarTheme.png?fit=293%2C186&amp;ssl=1\" loading=\"lazy\" class=\"alignnone size-full wp-image-118\" src=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/PreTitleBarTheme.png?resize=293%2C186&#038;ssl=1\" alt=\"\" width=\"293\" height=\"186\" data-recalc-dims=\"1\" \/><\/p>\n<p>All of the content is themed, but the title bar isn&#8217;t. It doesn&#8217;t look the best. We can fix it, but unfortunately the only way to do that is to implement the title bar from scratch all on our own. <a href=\"https:\/\/engy.us\/blog\/2020\/01\/01\/implementing-a-custom-window-title-bar-in-wpf\/\">That is a whole separate journey that I go over in another post<\/a>.<\/p>\n<p>Here&#8217;s what we get with our custom title bar:<\/p>\n<p><img data-attachment-id=\"124\" data-permalink=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/darktitlebar-2\/\" data-orig-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/DarkTitleBar.png?fit=622%2C58&amp;ssl=1\" data-orig-size=\"622,58\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"DarkTitleBar\" data-image-description=\"\" data-medium-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/DarkTitleBar.png?fit=300%2C28&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/DarkTitleBar.png?fit=525%2C49&amp;ssl=1\" loading=\"lazy\" class=\"alignnone size-full wp-image-124\" src=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/DarkTitleBar.png?resize=525%2C49&#038;ssl=1\" alt=\"\" width=\"525\" height=\"49\" srcset=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/DarkTitleBar.png?w=622&amp;ssl=1 622w, https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2018\/10\/DarkTitleBar.png?resize=300%2C28&amp;ssl=1 300w\" sizes=\"(max-width: 525px) 100vw, 525px\" data-recalc-dims=\"1\" \/><\/p>\n<p>A pain to implement, but at least now it looks less like garbage.<\/p>\n<h2>Source reference<\/h2>\n<p>You can check out <a href=\"https:\/\/github.com\/RandomEngy\/VidCoder\">VidCoder&#8217;s source code<\/a> for reference (beta branch).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a recent Windows 10 update a toggle switch was added to allow the user to specify that they wanted &#8220;Dark&#8221; themes in apps: I decided to add support for this to VidCoder. But no updates to WPF were made to make this easy. WPF does having theming support where it can pull in different &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/engy.us\/blog\/2018\/10\/20\/dark-theme-in-wpf\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Dark Theme in WPF&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false},"categories":[1],"tags":[],"jetpack_featured_media_url":"","jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pahBcK-1q","jetpack-related-posts":[{"id":79,"url":"https:\/\/engy.us\/blog\/2018\/10\/03\/wpf-databinding-using-dynamicdata\/","url_meta":{"origin":88,"position":0},"title":"WPF Databinding using DynamicData","date":"October 3, 2018","format":false,"excerpt":"My project is MVVM and I had been using ReactiveList<T> as my go-to observable collection for viewmodel properties. But ReactiveUI deprecated ReactiveList in 8.6.1. So I needed to get on to the new recommended library: DynamicData. But there is no direct drop-in replacement you can do like ObservableCollection<T> <-> ReactiveList<T>.\u2026","rel":"","context":"With 2 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":43,"url":"https:\/\/engy.us\/blog\/2010\/03\/08\/saving-window-size-and-location-in-wpf-and-winforms\/","url_meta":{"origin":88,"position":1},"title":"Saving window size and location in WPF and WinForms","date":"March 8, 2010","format":false,"excerpt":"A common user interface tweak is to save the size and location of a window and restore that state when the program starts up again. That way, the users don\u2019t need to re-do their work of resizing and moving the windows to where they want them. However I\u2019ve found that\u2026","rel":"","context":"With 21 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":120,"url":"https:\/\/engy.us\/blog\/2020\/01\/01\/implementing-a-custom-window-title-bar-in-wpf\/","url_meta":{"origin":88,"position":2},"title":"Implementing a Custom Window Title Bar in WPF","date":"January 1, 2020","format":false,"excerpt":"There are several good reasons for wanting custom window chrome in WPF, such as fitting in additional UI or implementing a Dark theme. However the actual implementation is kind of tricky, since it is now your job to provide a bunch of features that you used to get for free.\u2026","rel":"","context":"With 12 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":165,"url":"https:\/\/engy.us\/blog\/2021\/10\/03\/efficient-svg-icons-in-web-components-with-webpack-and-svgo\/","url_meta":{"origin":88,"position":3},"title":"Efficient SVG icons in Web Components with Webpack and SVGO","date":"October 3, 2021","format":false,"excerpt":"So many ways to load them There are a lot of different ways to show an SVG on a webpage: <img>, <embed>, <object>, <iframe>, <canvas> and <svg> among them. I think for any halfway modern browser there are really only two serious contenders here. Referencing an SVG file: <img src=\"image.svg\"\u2026","rel":"","context":"Similar post","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":51,"url":"https:\/\/engy.us\/blog\/2015\/07\/17\/installing-net-framework-4-5-automatically-with-inno-setup\/","url_meta":{"origin":88,"position":4},"title":"Installing .NET Framework 4.7 automatically with Inno Setup","date":"July 17, 2015","format":false,"excerpt":"In this guide I will walk through how to get the .NET framework to download and install on-the-fly in an Inno Setup installer. It works in 3 steps: Detect if the desired .NET framework is installed Download the .NET Framework bootstrap installer with Inno Download Plugin Run the bootstrap installer\u2026","rel":"","context":"In \".net framework 4.5 inno setup\"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2015\/07\/7028.InstallingFramework2-1.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":146,"url":"https:\/\/engy.us\/blog\/2021\/02\/28\/installing-net-5-runtime-automatically-with-inno-setup\/","url_meta":{"origin":88,"position":5},"title":"Installing .NET 5 Runtime Automatically with Inno Setup","date":"February 28, 2021","format":false,"excerpt":"In this guide I will walk through how to get the .NET 5 runtime to download and install on-the-fly in an Inno Setup installer. It works in 3 steps: Detect if the desired .NET runtime is installedDownload the .NET Runtime bootstrap installer with Inno Download PluginRun the bootstrap installer in\u2026","rel":"","context":"With 2 comments","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts\/88"}],"collection":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/comments?post=88"}],"version-history":[{"count":7,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts\/88\/revisions"}],"predecessor-version":[{"id":125,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts\/88\/revisions\/125"}],"wp:attachment":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/media?parent=88"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/categories?post=88"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/tags?post=88"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}