Creating a Dark Mode for Windows Forms Applications

I recently decided to spruce up one of my Windows Forms applications, to make it more modern and Windows 10-like.

The original look and feel was old fashioned:

I know what you’re thinking: “Windows Forms? In this day and age? Why not a web app? Why not WPF?” But this is Syncify - a desktop app to re-title MP3 files. And I am way more productive in Windows Forms…

As part of the face-lift I decided to create a dark mode.

This is what I implemented:
  1. Detect whether the user has dark mode selected. On Windows 10 this is a case of checking the registry for the newer Light Mode. If that is not selected, then my app uses its dark mode.
    private static bool UsingLightTheme()
    {
        var registryKey = Registry.CurrentUser.OpenSubKey(
            @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
        var appsUseLightTheme = registryKey?.GetValue("AppsUseLightTheme");
     
        if (appsUseLightTheme is null)
        {
            return true;
        }
        else
        {
            return Convert.ToBoolean(appsUseLightTheme, 
                CultureInfo.InvariantCulture);
        }
    }
  2. Now I need to alter the appearance of the title bar on the form. Sadly, this is not possible in C#, we must use the Windows API:
    [DllImport("dwmapi.dll")]
    private static extern int DwmSetWindowAttribute(IntPtr hwndint attr, 
        ref int attrValueint attrSize);
     
    private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
    private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
     
    internal static bool UseImmersiveDarkMode(IntPtr handlebool enabled)
    {
        if (IsWindows10OrGreater(17763))
        {
            var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
            if (IsWindows10OrGreater(18985))
            {
                attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
            }
     
            int useImmersiveDarkMode = enabled ? 1 : 0;
            return DwmSetWindowAttribute(handle, (int)attribute, 
                ref useImmersiveDarkModesizeof(int)) == 0;
        }
     
        return false;
    }
     
    private static bool IsWindows10OrGreater(int build = -1)
    {
        return Environment.OSVersion.Version.Major >= 10 && 
            Environment.OSVersion.Version.Build >= build;
    }
    NB - you need to include an app.manifest file in order for Environment.OSVersion to return the correct value.
  3. Next, I went ahead and added some panels and changed the colour and style of the form controls, using the designer in Visual Studio. Essentially this boiled down to setting dark background colours (shades of grey) and light foreground colours (white text and a blue action button). The controls also look better flat with no border.
  4. Then I cut the changed values out of the form’s designer file and pasted them into my DarkMode method.
  5. Last of all, I refactored them so that they resemble more of a Cascading Stylesheet. With reusable definitions for the styles and colours:
    // styles for dark mode
    var backgroundColor = Color.FromArgb(121212);
    var foregroundColor = Color.White;
    var panelColor = Color.FromArgb(282828);
    var buttonColor = Color.FromArgb(444444);
    var actionButtonColor = Color.DodgerBlue;
    var buttonBorderSize = 0;
    var buttonFlatStyle = FlatStyle.Flat;
    var textboxBorderStyle = BorderStyle.None;
     
    // properties taken from the form designer
    this.folderTextBox.BackColor = backgroundColor;
    this.folderTextBox.BorderStyle = textboxBorderStyle;
    this.folderTextBox.ForeColor = foregroundColor;
     
    this.browseButton.BackColor = buttonColor;
    this.browseButton.FlatAppearance.BorderSize = buttonBorderSize;
    this.browseButton.FlatStyle = buttonFlatStyle;
     
    this.goButton.BackColor = actionButtonColor;
    this.goButton.FlatAppearance.BorderSize = buttonBorderSize;
    this.goButton.FlatStyle = buttonFlatStyle;
     
    this.logTextBox.BackColor = backgroundColor;
    this.logTextBox.BorderStyle = textboxBorderStyle;
    this.logTextBox.ForeColor = foregroundColor;
     
    this.retitle.BackColor = panelColor;
    this.removeImages.BackColor = panelColor;
    this.leftPanel.BackColor = panelColor;
    this.topPanel.BackColor = panelColor;
     
    this.BackColor = backgroundColor;
    this.ForeColor = foregroundColor;
The result is an application built in Windows Form with C# that looks great on Windows 10, in both Light Mode:

And even better, in my preferred theme, Dark Mode:

Happy coding!

Comments