Code snippets referencing system colors from the active high contrast theme, in five UI frameworks


This post demonstrates how to reference appropriate system colors for button text and button background when a high contrast theme is active. The snippets relate to HTML, XAML, WinForms, WPF and Win32.

 

Introduction

A couple of weeks ago I asked a dev about the UI frameworks that were being used in his feature’s UI. He told me that the feature used Win32, WinForms, WPF and some HTML. This was a reminder to me how varied the implementation of accessibility is for some teams. While the goals around accessibility are the same regardless of the UI framework being used, the code that a dev writes is specific to the UI framework.

The conversation then moved to the topic of support for high contrast themes, and I subsequently built a VS solution with projects built with each of the five UI frameworks of HTML, XAML, Win32, WinForms and WPF. The HTML and XAML projects were UWP Store apps, (and often the steps around HTML UWP accessibility are the same for HTML hosted in the Edge browser). By default each project presented a button with custom colors for its text and background, yet presented appropriate system colors when a high contrast theme is active.

The sections below show the relevant snippets from each project. While I’ve not included all the ways of accessing the system colors, (eg including both markup and code-behind in the XAML app,) the snippets below show some common ways of getting the button colors that your customers need.

 

Referencing system colors from the active high contrast theme

At the end of each section below, I’ve included a screenshot of each button shown with the default theme, the High Contrast Black theme, and the High Contrast White theme.

Note: The snippets below are only aimed at showing how some system colors for the active theme can be accessed. As they are, the snippets leave the custom buttons only partially implemented. For example, the HTML and Win32 buttons show no mouse hover feedback in the default theme, and the Win32 button shows no keyboard focus feedback when a high contrast theme is active.

Important: For a shipping app, I’d definitely add a border to the Win32 button when a high contrast theme is active, regardless of whether a border is shown with the default theme. When a high contrast theme is active, I want the button to be very clearly delineated against its surroundings.

 

HTML

The css below specifies that system colors should be shown on the button with id “MyButton” when a high contrast theme is active.

 

    #MyButton {
        color: Crimson;
        background: LightBlue;
    }

    @media screen and (-ms-high-contrast)
    {
        #MyButton {
            color: ButtonText;
            background: ButtonFace;
        }
    }

 

 

HTML button shown in the default theme.

Figure 1: HTML button shown in the default theme.

 

HTML button shown in the High Contrast Black theme.

Figure 2: HTML button shown in the High Contrast Black theme.

 

HTML button shown in the High Contrast White theme.

Figure 3: HTML button shown in the High Contrast White theme.

 

 

XAML

The resource dictionary below references system colors to be used with ThemeResources when a high contrast theme is active, and those ThemeResources are referenced by a button’s XAML.

 

<Button x:Uid=”MyButton”
    Background=”{ThemeResource MyButtonBackground}”
    Foreground=”{ThemeResource MyButtonForeground}”/>

 

<ResourceDictionary>

    <ResourceDictionary.ThemeDictionaries>
        <ResourceDictionary x:Key=”Default”>
            <SolidColorBrush x:Key=”MyButtonForeground”

                Color=”Crimson” />
            <SolidColorBrush x:Key=”MyButtonBackground”

                Color=”LightBlue” />
        </ResourceDictionary>
        <ResourceDictionary x:Key=”Light”>
            <SolidColorBrush x:Key=”MyButtonForeground”

                Color=”Crimson” />
            <SolidColorBrush x:Key=”MyButtonBackground”

                Color=”LightBlue” />
        </ResourceDictionary>
        <ResourceDictionary x:Key=”HighContrast”>
            <SolidColorBrush x:Key=”MyButtonForeground”

                Color=”{ThemeResource SystemColorButtonTextColor}” />
            <SolidColorBrush x:Key=”MyButtonBackground”

                Color=”{ThemeResource SystemColorButtonFaceColor}” />
        </ResourceDictionary>

    </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

 

 

XAML button shown in the default theme.

Figure 4: XAML button shown in the default theme.

 

XAML button shown in the High Contrast Black theme.

Figure 5: XAML button shown in the High Contrast Black theme.

 

XAML button shown in the High Contrast White theme.

Figure 6: XAML button shown in the High Contrast White theme.

 

 

Win32

The snippet below relates to rendering a button’s text and background, when the button has the style BS_OWNERDRAW. Note that this snippet is just an example, and doesn’t do the work to show the button in various states, or to center the text inside the button. The snippet’s only intended to show how system colors for the button are accessed.

 

case WM_DRAWITEM:
{
    LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
    if (pDIS->CtlID == IDC_MYBUTTON)
    {
        COLORREF rgbButtonText = RGB(220, 20, 60); // Crimson.
        COLORREF rgbButtonBackground = RGB(173, 216, 230); // LightBlue.

        // Get details on the current state of high contrast.
        HIGHCONTRAST hcData = { 0 };
        hcData.cbSize = sizeof(hcData);

        if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &hcData, 0))
        {
            // Is a high contrast theme currently active?
            if (hcData.dwFlags & HCF_HIGHCONTRASTON)
            {
                rgbButtonText = GetSysColor(COLOR_BTNTEXT);
                rgbButtonBackground = GetSysColor(COLOR_BTNFACE);
            }

        }

        // Set the appropriate colors on the owner-drawn button.
        SetTextColor(pDIS->hDC, rgbButtonText);
        SetBkColor(pDIS->hDC, rgbButtonBackground);

        // Now draw the button’s contents.
        RECT rcControl;
        GetClientRect(pDIS->hwndItem, &rcControl);

        WCHAR staticText[64];
        int len = SendMessage(pDIS->hwndItem, WM_GETTEXT, ARRAYSIZE(staticText), (LPARAM)staticText);
        ExtTextOut(pDIS->hDC, pDIS->rcItem.left, pDIS->rcItem.top,

            ETO_OPAQUE, &rcControl, staticText, len, NULL);
    }

    return TRUE;
}

 

 

Win32 button shown in the default theme.

Figure 7: Win32 button shown in the default theme.

 

Win32 button shown in the High Contrast Black theme.

Figure 8: Win32 button shown in the High Contrast Black theme.

 

Note: Like I said before, for a shipping button, I’d be sure to draw a border around the button when a high contrast theme is active. I’d draw the border using the same color as that used for the button text.

 

Win32 button shown in the High Contrast White theme.

Figure 9: Win32 button shown in the High Contrast White theme.

 

 

WinForms

The code-behind below not only accesses the appropriate system colors for the button when a high-contrast theme is active, but also reacts to a change in state of active theme. This means that when a high contrast theme is made active or inactive, the button continues to show the appropriate system colors.

 

using Microsoft.Win32;
using System.Drawing;
using System.Windows.Forms;

 

    // Set the appropriate colors when the form is loaded, and set up an
    // event handler to be notified in a change in state of high contrast.

    private void Form1_Load(object sender, EventArgs e)
    {
        AccountForHighContrastTheme();

        SystemEvents.UserPreferenceChanged +=
                this.SystemEvents_UserPreferenceChanged;

    }

 

    // Make sure the appropriate colors are shown following a change in
    // the state of high contrast.
    private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
    {
        AccountForHighContrastTheme();
    }

 

    private void AccountForHighContrastTheme()
    {
        // Is a high contrast theme currently active?
        if (SystemInformation.HighContrast)
        {
            // Show whatever system colors are appropriate for the current high contrast theme.
            this.MyButton.BackColor = SystemColors.Control;
            this.MyButton.ForeColor = SystemColors.ControlText;
        }

        else
        {
            // A high contrast theme is not active, so show some custom colors.
            this.MyButton.BackColor = Color.LightBlue;
            this.MyButton.ForeColor = Color.Crimson;
        }
    }

 

 

WinForms button shown in the default theme.

Figure 10: WinForms button shown in the default theme.

 

WinForms button shown in the High Contrast Black theme.

Figure 11: WinForms button shown in the High Contrast Black theme.

 

WinForms button shown in the High Contrast White theme.

Figure 12: WinForms button shown in the High Contrast White theme.

 

 

WPF

The code-behind below shows how to access the appropriate system colors when a high contrast theme is active. Note that the button does not change its visuals if the active theme is changed while the button is displayed. So the screenshots below were generated by presenting the button while the desired high contrast theme was already active.

 

using System.Windows;

 

if (SystemParameters.HighContrast)
{
    MyButton.Foreground = SystemColors.ControlTextBrush;
    MyButton.Background = SystemColors.ControlBrush;​
}
else
{
    MyButton.Foreground = Brushes.Crimson;
    MyButton.Background = Brushes.LightBlue;
}

 

 

WPF button shown in the default theme.

Figure 13: WPF button shown in the default theme.

 

WPF button shown in the High Contrast Black theme.

Figure 14: WPF button shown in the High Contrast Black theme.

 

WPF shown in the High Contrast White theme.

Figure 15: WPF shown in the High Contrast White theme.

 

 

Summary

When a high contrast theme is active, never hard-code colors for button text and button background. I’ve seen an in-development feature check whether a high contrast theme is active, and then hard-code some part of the UI to black. This happened to look fine in one high contrast theme, but left a UI element invisible in another. What’s more – some customers will customize the colors shown in the high contrast theme that they use, and you can’t account for those customizations with hard-coded colors.

So when you need to explicitly set colors on UI elements, always reference system colors when a high contrast theme is active, and feel confident that the UI will appear exactly as your customers expect, regardless of the actual colors shown.

And as always, consider whether you really need to show custom colors on buttons in the first place. Like many standard controls, if a standard button is used, then it’s very likely accessible by default, and that includes showing the colors that your customers expect when a high contrast theme is active.

Guy


Comments (0)

Skip to main content