How to make WinForm use the system dark mode theme? - c#

Can WinForm use system dark mode theme like explorer or StartAllBack?
I've tried these things:
int trueValue = 0x01, falseValue = 0x00;
SetWindowTheme(this.Handle, "DarkMode_Explorer", null);
DwmSetWindowAttribute(this.Handle, DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE, ref trueValue, Marshal.SizeOf(typeof(int)));
DwmSetWindowAttribute(this.Handle, DwmWindowAttribute.DWMWA_MICA_EFFECT, ref trueValue, Marshal.SizeOf(typeof(int)));
But these only make the titlebar and scrollbar in dark, no difference for other controls.
How to make all controls to system dark style, like this:
Dark mode theme Explorer

Related

Change Windows Appearance programmatically

I need to change Windows Accent Color using C#. I need to change only these 3 settings (accent color, show accent color in start, taskbar, action center and title bars and window borders.)
If possible, I would like to take these effects immediately.
I tried this:
Color newColor = Color.FromArgb(255, 0, 0); // red color
RegistryKey key = Registry.CurrentUser.CreateSubKey(#"Control Panel\Personalization");
key.SetValue("AccentColor", ColorTranslator.ToWin32(newColor));
key.Close();
const uint SPI_SETDESKWALLPAPER = 20;
const uint SPIF_UPDATEINIFILE = 0x1;
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, 0, SPIF_UPDATEINIFILE);
I tried it with and without administrator rights, I tried restarting PC but nothing changed. I use Windows 10 Home.
I also asked ChatGPT, but all solutions ChatGPT provided was not working as expected.
So is there a way to change these settings programmatically? Changing registry? Any help will be useful, thanks.

How to set color of window title depending on the color of the WindowChrome

I need to have the window title of my application change color depending on what color is set in Windows 10 for the WindowChrome.
Is there a WPF property to bind to for the text color that automatically assumes the correct color (either black or white) that is actually legible?
Do you have to calculate the correct color manually and if so, how do you determine the color of the WindowChrome?
In previous versions of Windows, you would use the SystemColors class to get the corresponding colors. However, it does not map to the Windows 10 light and dark mode colors, as you can see in this issue. It will only be useful in in high contrast mode to a certain extent. The SystemParameters.WindowGlassBrush will only give you the accent color, which is not the window chrome color in general, but only if you explicitly apply it to window frames and titles in the system settings.
I have not found any way to either get the window chrome color or the title bar color directly, anywhere.
What you can do instead, is detect if Light Mode or Dark Mode is enabled for apps and change the color in your application depending on it, but you have to define the colors yourself. The following snippet uses the registry to find out which mode is enabled. If the registry key is not defined, it will return null.
var value = Registry.GetValue(#"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", null);
var isAppLightTheme = value != null ? Convert.ToBoolean(value) : (bool?)null;
If you also want to cover the case when the access color is set as window frame or title bar color, use the registry entry below. The problem in this scenario is that Windows will set the title text color to either black or white, depending on the accent color for better readability. As above, you have to handle this manually.
var value = Registry.GetValue(#"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", "ColorPrevalence", null);
var isTitleFrameAccentColored = value != null ? Convert.ToBoolean(value) : (bool?)null;
In order to access the registry, you need the Microsoft.Win32 namespace. If you use .NET Core you can install the Microsoft.Win32.Registry package to access the Registry class.

windows phone 8.1 header bar icon colours

My application is setting the Requested theme in the App.xaml as we only want to show a light theme regardless of what the user's system theme is.
Problem is, when the user is on a dark theme (which has white icons for signal/battery/time etc) the app switches all style resources to use light theme but does nothing about the header bar. This creates the situation where you have a white page background and white icons on top of it.
Is there a way to change the theme applied to this top bar?
I tried adding a dark colour behind the bar (30px high rectangle with margin -30 on top) but the behaviour happens on light theme too so then if the user is on light theme, the icons are black and too dark to see on the colour background.
It's not obvious in the picture, but on top of that map there is the header bar with icons but the icons are white and the theme says page background is white.
Turns out the correct answer is:
http://msdn.microsoft.com/library/windows/apps/windows.ui.viewmanagement.statusbar(v=win.10).aspx
This class also cannot be used from the XAML so you have to do:
public MainPage()
{
StatusBar statusBar = StatusBar.GetForCurrentView();
statusBar.ForegroundColor = new Windows.UI.Color() { A = 0xFF, R = 0xFF, G = 0x00, B = 0xAA };
this.InitializeComponent();
}

How to create shell styled title bar buttons in .NET

This is a follow-up to my earlier question Draw Custom Buttons on Windows Vista/7 Aero Titlebar.
I revisited the topic quite recently and found this article which is essentially a hack to 'drawing' buttons on Aero-enabled title bar (Windows Vista & 7). What the code does is to create a transparent window over the current one and places the buttons on it, giving the impression of additional buttons on the title bar. The only problem is the buttons look like regular WinForms buttons!
My question is, how do I read the windows shell style (aka theme) in order to create buttons styled just like those in the Control Box (see image)?
I'd like answers to be in .NET (VB.NET or C#). I'm okay with unmanaged code.
So if I understand you correctly, you want to read what Windows 7 calls the "Window Color" aspect of the current theme.
Acording to MSDN http://msdn.microsoft.com/en-us/magazine/cc163435.aspx, you want DwmGetColorizationColor: "retrieves the current color that is being used for DWM glass composition. This value is based on the current color scheme. Changing the setting causes a WM_WMCOLORIZATIONCOLORCHANGED notification."
[DllImport("dwmapi.dll", PreserveSig = false)]
public static extern void DwmGetColorizationColor(out int pcrColorization, [MarshalAs(UnmanagedType.Bool)]out bool pfOpaqueBlend);
"You can check to see the composition color and opacity by calling the DwmGetColorizationColor function. If this function succeeds, it will set a GDI+ ARGB color value and a Boolean indicating whether the color is opaque. Just like changing the Aero scheme in the control panel, there's a message broadcast when the composition color has changed. WM_DWMCOLORIZATIONCOLORCHANGED is sent when this happens, but in this case the parameters tell you what the new color and opacity are."

DwmExtendFrameIntoClientArea without Aero Glass

Using the DwmExtendFrameIntoClientArea API call with Aero Glass enabled works just fine. However, I want it to work when Aero Glass is disabled as well, like how it works in the Windows control panel:
Notice how the frame has extended into the client area, even though Aero Glass is disabled? When I make the DwmExtendFrameIntoClientArea API call in my application, the returned HRESULT is definitely not success, and my application ends up looking like this:
http://img197.imageshack.us/img197/9629/clientapplication.png
Normally, with Aero Glass enabled, the border stretches down to underneath the navigation buttons, like in the control panel. How do I do this? DwmExtendFrameIntoClientArea clearly isn't working.
By the way, if it is relevant, my application is a WPF application.
Nir's answer is correct; when composition is disabled you have to draw that area yourself.
i can show you the code i have in the paint handler of the panel at the top of my form - the panel normally responsible for drawing the 0x00000000 transparent black to make the glass appear:
Psuedo-code:
procedure DrawGlassHeaderArea(g: Graphics; r: Rectangle; IsFormFocused: Boolean);
const
clFakeGlassColor = $00EAD1B9; //(185, 209, 234) This is the fake foreground glass color (for use when composition is disabled)
clFakeGlassColorUnfocused = $00F2E4D7; //(215, 228, 242) This is the fake background glass color (for use when composition is disabled)
begin
if Dwm.IsCompositionEnabled then
begin
g.FillRectangle(r, 0x00000000); //fill rectangle with transparent black
end
else
//Composition disabled; fake it like Microsoft does
//The color to use depends if the form has focused or not
Color glassColor;
if (IsFormFocused) then
c = clFakeGlassColor
else
c = clFakeGlassColorUnfocused;
g.FillRectangle(r, glassColor); //fill rectangle with fake color
//Now we have to draw the two accent lines along the bottom
Color edgeHighlight = ColorBlend(Colors.White, glassColor, 0.33); //mix 33% of glass color to white
Color edgeShadow = ColorBlend(Colors.Black, glassColor, 0.33); //mix 33% of glass color to black
//Draw highlight as 2nd-last row:
g.DrawLine(edgeHighlight, Point(r.Left, r.Bottom-2), Point(r.Right, r.Bottom-2);
//Draw shadow on the very last row:
g.DrawLine(edgeHighlight, Point(r.Left, r.Bottom-1), Point(r.Right, r.Bottom-1);
end;
end;
Sample usage
procedure MyForm.PaintBox1Paint(PaintEventArgs e)
begin
DrawGlassHeaderArea(e.Graphics, PaintBox1.ClientRectangle, this.HasFocus);
end;
Bonus Screenshot
Update 7/9/2014
#JakePetroules was right, and i was wrong. The "blue" used for fake glass is not hard-coded into Windows. And it is accessible using GetThemeColor.
I coded up all the available colors (TMT_COLOR) available for a Window class:
Note: For more information about Classes, Parts, and States, see Aero Style Classes, Parts, and States
When using:
Class: Window
Part: WP_CAPTION
State: n/a (StateID is not used for the Caption part, nor the entire Window class)
and fetch the color code propertyID:
TMT_FILLCOLORHINT: for when the window has focus
TMT_BORDERCOLORHINT: for when the window does not have focus
you get the two important colors:
The pseudo-code i now use to get the fake glass color:
public Color GetFakeClassColor(Boolean isWindowFocused=true)
{
static Color fakeGlass= 0x00B8D0E9; //the correct answer anyway
if ((GetThemeAppProperties() && STAP_ALLOW_CONTROLS) == 0)
return fakeGlass;
hTheme = OpenThemeData(GetDesktopWindow(), "Window");
if (hTheme = 0)
return fakeGlass;
Int32 propID;
if (isWindowFocused)
propID= TMT_FILLCOLORHINT; //The color used as a fill color hint for custom controls.
else
propID= TMT_BORDERCOLORHINT; //The color used as a border color hint for custom controls.
DWORD rgb;
if (Failed(GetThemeColor(hTheme, WP_CAPTION, 0, propID, ref rgb))
return fakeGlass;
Result = new Color(rgb);
}
In reality, since i use Delphi, my actual code is:
function GetFakeGlassColor(IsWindowFocused: Boolean=True): TColor;
var
ted: TThemedElement;
hTheme: THandle;
propID: Integer;
rgb: DWORD;
begin
Result := $00B8D0E9; //the correct answer anyway
//We can't use the ThemeServcies.ThemesEnabled, as that mistakenly checks for version 6 of the common controls library
//Themes can be enabled without using ComCtl V6, or common controls at all
if not ThemeServices.ThemesAvailable then
Exit;
if (GetThemeAppProperties and STAP_ALLOW_CONTROLS) = 0 then
Exit;
htheme := ThemeServices.Theme[teWindow];
if hTheme = 0 then
Exit;
if IsWindowFocused then
propID := TMT_FILLCOLORHINT //The color used as a fill color hint for custom controls.
else
propID := TMT_BORDERCOLORHINT; //The color used as a border color hint for custom controls.
if Failed(GetThemeColor(hTheme, WP_CAPTION, 0, propID, {var}rgb)) then
Exit;
Result := rgb;
end;
You have to paint it to be frame-like yourself.
You have to use DwmIsCompositionEnabled to check if the DWM is enabled and handle WM_DWMCOMPOSITIONCHANGED to detect DWM state changed.
Then you have to have to separate way of drawing the window, if DWM is enabled you use DwmExtendFrameIntoClientArea, if it's disabled you draw the "frame" yourself.
I have no idea how to duplicate the Aero frame in WPF (in my app I have my own color scheme and I'm not using the Auro frame).
This is annoying but when the DWM is disabled the system falls back to XP-style drawing and none of the DWM's services work - even those that aren't related to the glass effect.
You need to paint the window background yourself. You should not actually hard-code the colors as previous posts have suggested, but use the theme functions to retrieve them, like so (semi-pseudocode):
DWORD rgb;
HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
GetThemeColor(hTheme, WP_CAPTION, CS_ACTIVE,
<is active window> ? TMT_FILLCOLORHINT : TMT_BORDERCOLORHINT, &rgb);
// Can use these functions to retrieve the individual RGB values
BYTE r = GetRValue(rgb);
BYTE g = GetGValue(rgb);
BYTE b = GetBValue(rgb);
These colors will remain correct even if the user changes title bar colors in the control panel (unlike using COLOR_ACTIVECAPTION / COLOR_GRADIENTACTIVECAPTION). You should also check that themes are active using IsThemeActive() before attempting to get theme colors.
The values of the constants for quick reference:
WP_CAPTION: 1
CS_ACTIVE: 1
TMT_FILLCOLORHINT: 3821
TMT_BORDERCOLORHINT: 3822

Categories