|
| 1 | +ThemeSettings |
| 2 | +=== |
| 3 | + |
| 4 | +# Background |
| 5 | +_This section is only for internal use, it should not be part of the public documentation._ |
| 6 | + |
| 7 | +In UWP, apps can use the |
| 8 | +[AccessibilitySettings](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.accessibilitysettings?view=winrt-22621) |
| 9 | +API to query the system's high-contrast setting, and detect when it changes. But in a Win32/non-UWP context, the |
| 10 | +**AccessibilitySettings.HighContrastChanged** event never fires. This is because the event relies on the **CoreWindow** |
| 11 | +object to handle the `WM_THEMECHANGED` message and fire an internal event to the **AccessibilitySettings** object -- but |
| 12 | +in a non-UWP, Win32/WinAppSDK context, no **CoreWindow** exists. |
| 13 | + |
| 14 | +The API described in this doc, **ThemeSettings**, is a WinAppSDK WinRT API that provides the functionality of |
| 15 | +**AccessibilitySettings**, but for a Win32 environment. In the future we'd like to add other kinds of theme-related |
| 16 | +information to this API, so we decided to use a name that's not specific to accessibility or high-contrast. |
| 17 | + |
| 18 | +The **ThemeSettings.HighContrast** and **ThemeSettings.HighContrastScheme** properties shown below are basically identical to the |
| 19 | +existing UWP [AccessibilitySettings.HighContrast](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.accessibilitysettings.highcontrast?view=winrt-22621) |
| 20 | +and [AccessibilitySettings.HighContrastScheme](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.accessibilitysettings.highcontrastscheme?view=winrt-22621) |
| 21 | +properties. They're all implemented by calling the Win32 [SystemParametersInfo](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow) |
| 22 | +function with the `SPI_GETHIGHCONTRAST` flag. |
| 23 | + |
| 24 | +Note that **HighContrastScheme** will, like **AccessibilitySettings.HighContrastScheme**, continue to return a valid |
| 25 | +string value even when **HighContrast** is false. The Win10 Settings UI had two controls, a toggle for high-contrast |
| 26 | +enablement and a combo box to select the HC theme, the UWP **AccessibilitySettings** API was probably designed to match |
| 27 | +that. Rather than change the model, we stick with what the UWP API already exposed, since we know it works for app |
| 28 | +developers and can be implemented very simply on Windows. |
| 29 | + |
| 30 | +This GIF shows how a user can change the HighContrast theme in the Settings app today, and a System Xaml app that displays the current **HighContrastScheme**: |
| 31 | + |
| 32 | + |
| 33 | + |
| 34 | +### Why does this API require a WindowId? |
| 35 | +This API requires a **WindowId** object because the implementation listens for a `WM_THEMECHANGED` message. (This is |
| 36 | +the same message the **CoreWindow** listens for that drives the UWP **AccessibilitySettings.HighContrastChanged** event). |
| 37 | +An alternative option is for the API to create and manage its own top-level window, but we decided to use an existing |
| 38 | +window so we could have better control over the order in which these kinds of WinAppSDK events are fired. |
| 39 | + |
| 40 | +### Future directions |
| 41 | +The expectation is to add more theme APIs here that we should have full APIs for: |
| 42 | + |
| 43 | +* We need an API for light/dark mode, which currently you have to read from the registry. |
| 44 | +* System colors currently exposed by GetSysColor (Win32). |
| 45 | +* Accent colors currently exposed by UISettings.GetColorValue. |
| 46 | + |
| 47 | +# API Pages |
| 48 | + |
| 49 | +## ThemeSettings class |
| 50 | +The **ThemeSettings** API allows Win32 WinRT apps to detect that the system's High Contrast setting |
| 51 | +has changed. |
| 52 | + |
| 53 | +In this sample code, an app uses a **ThemeSettings** object to listen for high contrast changes |
| 54 | +on the system: |
| 55 | + |
| 56 | +```c++ |
| 57 | +void MyApp::ListenForHighContrastChange(XamlRoot& xamlRoot) |
| 58 | +{ |
| 59 | + const auto myWindowId = xamlRoot.ContentEnvironment().AppWindowId(); |
| 60 | + m_themeSettings = ThemeSettings::CreateForWindowId(myWindowId); |
| 61 | + |
| 62 | + m_themeSettings.Changed([xamlRoot](const ThemeSettings& ts, const auto&) { |
| 63 | + if (ts.HighContrast()) |
| 64 | + { |
| 65 | + ::OutputDebugString(L"High contrast is ON, scheme is: "); |
| 66 | + ::OutputDebugString(ts.HighContrastScheme().c_str()); |
| 67 | + } |
| 68 | + else |
| 69 | + { |
| 70 | + ::OutputDebugString(L"High contrast is OFF.\n"); |
| 71 | + } |
| 72 | + }); |
| 73 | +} |
| 74 | +``` |
| 75 | +
|
| 76 | +When the app releases all its references to a **ThemeSettings** object, the object will be destroyed and the **Changed** event will |
| 77 | +no longer fire. |
| 78 | +
|
| 79 | +### Methods |
| 80 | +|Name|Description| |
| 81 | +|-|-| |
| 82 | +|static CreateForWindowId(WindowId)|Creates a ThemeSettings object that will provide theme information for the given WindowId.| |
| 83 | +
|
| 84 | +### Events |
| 85 | +|Name|Description| |
| 86 | +|-|-| |
| 87 | +|Changed|Fired when one of the object's properties has changed.| |
| 88 | +
|
| 89 | +### Properties |
| 90 | +| Name | Description | |
| 91 | +|-|-| |
| 92 | +| Boolean HighContrast | True if High Contrast mode is enabled on the system. | |
| 93 | +| String HighContrastScheme | If HighContrast is true, a string from the system representing the High Contrast scheme, or style. | |
| 94 | +
|
| 95 | +## ThemeSettings.CreateForWindowId static method |
| 96 | +
|
| 97 | +``` c# |
| 98 | +static CreateForWindowId(WindowId windowId) |
| 99 | +``` |
| 100 | + |
| 101 | +Creates a **ThemeSettings** object that will provide theme information for the given **WindowId**. |
| 102 | + |
| 103 | +The given **WindowId** must represent a window that: |
| 104 | +* is a top-level window, |
| 105 | +* AND is on the same process and thread as the caller. |
| 106 | + |
| 107 | +For example, you can get a **WindowId** from the **ContentEnvironment.AppWindowId** property. You can get a **ContentEnvironment** |
| 108 | +object from a **XamlRoot** object. |
| 109 | + |
| 110 | +## ThemeSettings.Changed event |
| 111 | + |
| 112 | +```c# |
| 113 | +event TypedEventHandler<ThemeSettings, Object> Changed |
| 114 | +``` |
| 115 | + |
| 116 | +Fired when one of the object's properties has changed. |
| 117 | + |
| 118 | +The **Changed** event will only fire while the **ThemeSettings** object is alive, so apps must ensure |
| 119 | +they hold a reference to the object as long as they want to receive the event. |
| 120 | + |
| 121 | +The **Changed** event will only fire as long as the window represented by **WindowId** is alive (has not yet received `WM_NCDESTROY`). |
| 122 | + |
| 123 | +# API Details |
| 124 | + |
| 125 | +_Spec note: this is a new namespace, **Microsoft.UI.System**. |
| 126 | +The predecessor for this type is `AccessibilitySettings`, |
| 127 | +which is in the `Windows.UI.ViewManagement` namespace, |
| 128 | +which is all UWP types we don't plan to carry into Desktop. |
| 129 | +This is a UI type that doesn't fit into any of the children of the `UI` namespace. |
| 130 | +We anticipate more types here, but don't have any current plans._ |
| 131 | + |
| 132 | + |
| 133 | +``` c# (really midl3) |
| 134 | +namespace Microsoft.UI.System |
| 135 | +{ |
| 136 | + runtimeclass ThemeSettings |
| 137 | + { |
| 138 | + static ThemeSettings CreateForWindowId(WindowId windowId); |
| 139 | + |
| 140 | + event TypedEventHandler<ThemeSettings, Object> Changed; |
| 141 | + |
| 142 | + Boolean HighContrast { get; } |
| 143 | + String HighContrastScheme { get; } |
| 144 | + } |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +# Appendix |
| 149 | +_This section is only for internal use, it should not be part of the public documentation._ |
| 150 | + |
| 151 | +## Future Directions |
| 152 | +In the future, when we support cross-process and/or cross-thread islands, a WindowId won't be sufficient anymore. We'll need |
| 153 | +a static method that allows the app to create a ThemeSettings object for a given island. |
| 154 | + |
| 155 | +## Similar APIs |
| 156 | +Note in .net there is a |
| 157 | +[SystemEvents](https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.systemevents?view=dotnet-plat-ext-7.0) type |
| 158 | +that provides related APIs. This is implemented by creating a top-level window to receive `WM_THEMECHANGED` and other |
| 159 | +messages. That implementation is |
| 160 | +[here](https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System/compmod/microsoft/win32/SystemEvents.cs). |
0 commit comments