using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace VRCX { //Based off DWMWA_USE_IMMERSIVE_DARK_MODE, documentation: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute //dwAttribute was 19 before Windows 20H1, 20 after Windows 20H1 internal static class WinformThemer { /// /// Private holder of current theme /// private static int currentTheme = 0; /// /// Sets the global theme of the app /// Light = 0 /// Dark = 1 /// public static void SetGlobalTheme(int theme) { currentTheme = theme; //Make a seperate list for all current forms (causes issues otherwise) List
forms = new List(); foreach(Form form in Application.OpenForms) { forms.Add(form); } SetThemeToGlobal(forms); } /// /// Gets the global theme of the app /// Light = 0 /// Dark = 1 /// public static int GetGlobalTheme() => currentTheme; /// /// Set given form to the current global theme /// /// public static void SetThemeToGlobal(Form form) { SetThemeToGlobal(new List() { form }); } /// /// Set a list of given forms to the current global theme /// /// public static void SetThemeToGlobal(List forms) { //Save current active form so we can refocus on this at the end var activeForm = Form.ActiveForm; //Show and focus on the invisible popup InvisPopupHandler.Show(); //For each form, set the theme, then move focus onto it to force refresh foreach(Form form in forms) { //Set the theme of the window SetThemeToGlobal(form.Handle); //Move focus onto it to force refresh if not minimized if (form.WindowState != FormWindowState.Minimized) form.Activate(); } //Close + Dispose the invisible popup InvisPopupHandler.Close(); //Restore focus to previous active form if(activeForm != null && activeForm.WindowState != FormWindowState.Minimized) activeForm.Activate(); } private static void SetThemeToGlobal(IntPtr handle) { if (GetTheme(handle) != currentTheme) { if (PInvoke.DwmSetWindowAttribute(handle, 19, new[] { currentTheme }, 4) != 0) PInvoke.DwmSetWindowAttribute(handle, 20, new[] { currentTheme }, 4); } } private static int GetTheme(IntPtr handle) { //Allocate needed memory IntPtr curThemePtr = Marshal.AllocHGlobal(4); //See what window state it currently is if (PInvoke.DwmGetWindowAttribute(handle, 19, curThemePtr, 4) != 0) PInvoke.DwmGetWindowAttribute(handle, 20, curThemePtr, 4); //Read current theme (light = 0, dark = 1) int theme = Marshal.ReadInt32(curThemePtr); //Free previously allocated Marshal.FreeHGlobal(curThemePtr); return theme; } internal static class InvisPopupHandler { private static InvisPopup instance; internal static void Show() { if(instance == null) instance = new InvisPopup(); instance.Show(); instance.Activate(); } internal static void Close() { instance.Close(); instance.Dispose(); instance = null; } } internal static class PInvoke { [DllImport("DwmApi")] internal static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, int[] pvAttribute, int cbAttribute); [DllImport("DwmApi")] internal static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, IntPtr pvAttribute, int cbAttribute); } } }