using System; using System.Configuration; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; namespace CoreUnit.Common { /// This tries to automatically apply Windows Dark Mode (if enabled) to a Form. /// Author: Blue Mystic - 2024 public class DarkModeCS { #region Win32 API Declarations public struct DWMCOLORIZATIONcolors { public uint ColorizationColor, ColorizationAfterglow, ColorizationColorBalance, ColorizationAfterglowBalance, ColorizationBlurBalance, ColorizationGlassReflectionIntensity, ColorizationOpaqueBlend; } [Flags] public enum DWMWINDOWATTRIBUTE : uint { /// /// Use with DwmGetWindowAttribute. Discovers whether non-client rendering is enabled. The retrieved value is of type BOOL. TRUE if non-client rendering is enabled; otherwise, FALSE. /// DWMWA_NCRENDERING_ENABLED = 1, /// /// Use with DwmSetWindowAttribute. Sets the non-client rendering policy. The pvAttribute parameter points to a value from the DWMNCRENDERINGPOLICY enumeration. /// DWMWA_NCRENDERING_POLICY, /// /// Use with DwmSetWindowAttribute. Enables or forcibly disables DWM transitions. The pvAttribute parameter points to a value of type BOOL. TRUE to disable transitions, or FALSE to enable transitions. /// DWMWA_TRANSITIONS_FORCEDISABLED, /// /// Use with DwmSetWindowAttribute. Enables content rendered in the non-client area to be visible on the frame drawn by DWM. The pvAttribute parameter points to a value of type BOOL. TRUE to enable content rendered in the non-client area to be visible on the frame; otherwise, FALSE. /// DWMWA_ALLOW_NCPAINT, /// /// Use with DwmGetWindowAttribute. Retrieves the bounds of the caption button area in the window-relative space. The retrieved value is of type RECT. If the window is minimized or otherwise not visible to the user, then the value of the RECT retrieved is undefined. You should check whether the retrieved RECT contains a boundary that you can work with, and if it doesn't then you can conclude that the window is minimized or otherwise not visible. /// DWMWA_CAPTION_BUTTON_BOUNDS, /// /// Use with DwmSetWindowAttribute. Specifies whether non-client content is right-to-left (RTL) mirrored. The pvAttribute parameter points to a value of type BOOL. TRUE if the non-client content is right-to-left (RTL) mirrored; otherwise, FALSE. /// DWMWA_NONCLIENT_RTL_LAYOUT, /// /// Use with DwmSetWindowAttribute. Forces the window to display an iconic thumbnail or peek representation (a static bitmap), even if a live or snapshot representation of the window is available. This value is normally set during a window's creation, and not changed throughout the window's lifetime. Some scenarios, however, might require the value to change over time. The pvAttribute parameter points to a value of type BOOL. TRUE to require a iconic thumbnail or peek representation; otherwise, FALSE. /// DWMWA_FORCE_ICONIC_REPRESENTATION, /// /// Use with DwmSetWindowAttribute. Sets how Flip3D treats the window. The pvAttribute parameter points to a value from the DWMFLIP3DWINDOWPOLICY enumeration. /// DWMWA_FLIP3D_POLICY, /// /// Use with DwmGetWindowAttribute. Retrieves the extended frame bounds rectangle in screen space. The retrieved value is of type RECT. /// DWMWA_EXTENDED_FRAME_BOUNDS, /// /// Use with DwmSetWindowAttribute. The window will provide a bitmap for use by DWM as an iconic thumbnail or peek representation (a static bitmap) for the window. DWMWA_HAS_ICONIC_BITMAP can be specified with DWMWA_FORCE_ICONIC_REPRESENTATION. DWMWA_HAS_ICONIC_BITMAP normally is set during a window's creation and not changed throughout the window's lifetime. Some scenarios, however, might require the value to change over time. The pvAttribute parameter points to a value of type BOOL. TRUE to inform DWM that the window will provide an iconic thumbnail or peek representation; otherwise, FALSE. Windows Vista and earlier: This value is not supported. /// DWMWA_HAS_ICONIC_BITMAP, /// /// Use with DwmSetWindowAttribute. Do not show peek preview for the window. The peek view shows a full-sized preview of the window when the mouse hovers over the window's thumbnail in the taskbar. If this attribute is set, hovering the mouse pointer over the window's thumbnail dismisses peek (in case another window in the group has a peek preview showing). The pvAttribute parameter points to a value of type BOOL. TRUE to prevent peek functionality, or FALSE to allow it. Windows Vista and earlier: This value is not supported. /// DWMWA_DISALLOW_PEEK, /// /// Use with DwmSetWindowAttribute. Prevents a window from fading to a glass sheet when peek is invoked. The pvAttribute parameter points to a value of type BOOL. TRUE to prevent the window from fading during another window's peek, or FALSE for normal behavior. Windows Vista and earlier: This value is not supported. /// DWMWA_EXCLUDED_FROM_PEEK, /// /// Use with DwmSetWindowAttribute. Cloaks the window such that it is not visible to the user. The window is still composed by DWM. Using with DirectComposition: Use the DWMWA_CLOAK flag to cloak the layered child window when animating a representation of the window's content via a DirectComposition visual that has been associated with the layered child window. For more details on this usage case, see How to animate the bitmap of a layered child window. Windows 7 and earlier: This value is not supported. /// DWMWA_CLOAK, /// /// Use with DwmGetWindowAttribute. If the window is cloaked, provides one of the following values explaining why. DWM_CLOAKED_APP (value 0x0000001). The window was cloaked by its owner application. DWM_CLOAKED_SHELL(value 0x0000002). The window was cloaked by the Shell. DWM_CLOAKED_INHERITED(value 0x0000004). The cloak value was inherited from its owner window. Windows 7 and earlier: This value is not supported. /// DWMWA_CLOAKED, /// /// Use with DwmSetWindowAttribute. Freeze the window's thumbnail image with its current visuals. Do no further live updates on the thumbnail image to match the window's contents. Windows 7 and earlier: This value is not supported. /// DWMWA_FREEZE_REPRESENTATION, /// /// Use with DwmSetWindowAttribute. Enables a non-UWP window to use host backdrop brushes. If this flag is set, then a Win32 app that calls Windows::UI::Composition APIs can build transparency effects using the host backdrop brush (see Compositor.CreateHostBackdropBrush). The pvAttribute parameter points to a value of type BOOL. TRUE to enable host backdrop brushes for the window, or FALSE to disable it. This value is supported starting with Windows 11 Build 22000. /// DWMWA_USE_HOSTBACKDROPBRUSH, /// /// Use with DwmSetWindowAttribute. Allows the window frame for this window to be drawn in dark mode colors when the dark mode system setting is enabled. For compatibility reasons, all windows default to light mode regardless of the system setting. The pvAttribute parameter points to a value of type BOOL. TRUE to honor dark mode for the window, FALSE to always use light mode. This value is supported starting with Windows 10 Build 17763. /// DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, /// /// Use with DwmSetWindowAttribute. Allows the window frame for this window to be drawn in dark mode colors when the dark mode system setting is enabled. For compatibility reasons, all windows default to light mode regardless of the system setting. The pvAttribute parameter points to a value of type BOOL. TRUE to honor dark mode for the window, FALSE to always use light mode. This value is supported starting with Windows 11 Build 22000. /// DWMWA_USE_IMMERSIVE_DARK_MODE = 20, /// /// Use with DwmSetWindowAttribute. Specifies the rounded corner preference for a window. The pvAttribute parameter points to a value of type DWM_WINDOW_CORNER_PREFERENCE. This value is supported starting with Windows 11 Build 22000. /// DWMWA_WINDOW_CORNER_PREFERENCE = 33, /// /// Use with DwmSetWindowAttribute. Specifies the color of the window border. The pvAttribute parameter points to a value of type COLORREF. The app is responsible for changing the border color according to state changes, such as a change in window activation. This value is supported starting with Windows 11 Build 22000. /// DWMWA_BORDER_COLOR, /// /// Use with DwmSetWindowAttribute. Specifies the color of the caption. The pvAttribute parameter points to a value of type COLORREF. This value is supported starting with Windows 11 Build 22000. /// DWMWA_CAPTION_COLOR, /// /// Use with DwmSetWindowAttribute. Specifies the color of the caption text. The pvAttribute parameter points to a value of type COLORREF. This value is supported starting with Windows 11 Build 22000. /// DWMWA_TEXT_COLOR, /// /// Use with DwmGetWindowAttribute. Retrieves the width of the outer border that the DWM would draw around this window. The value can vary depending on the DPI of the window. The pvAttribute parameter points to a value of type UINT. This value is supported starting with Windows 11 Build 22000. /// DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, /// /// The maximum recognized DWMWINDOWATTRIBUTE value, used for validation purposes. /// DWMWA_LAST, } [Flags] public enum DWM_WINDOW_CORNER_PREFERENCE { DWMWCP_DEFAULT = 0, DWMWCP_DONOTROUND = 1, DWMWCP_ROUND = 2, DWMWCP_ROUNDSMALL = 3 } [Serializable, StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; public Rectangle ToRectangle() { return Rectangle.FromLTRB(Left, Top, Right, Bottom); } } public const int EM_SETCUEBANNER = 5377; [DllImport("user32.dll", CharSet = CharSet.Auto)] public extern static IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); [DllImport("DwmApi")] public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, int[] attrValue, int attrSize); [DllImport("dwmapi.dll")] public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)] private extern static int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList); [DllImport("dwmapi.dll", EntryPoint = "#127")] public static extern void DwmGetColorizationParameters(ref DWMCOLORIZATIONcolors colors); [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")] private static extern IntPtr CreateRoundRectRgn ( int nLeftRect, // x-coordinate of upper-left corner int nTopRect, // y-coordinate of upper-left corner int nRightRect, // x-coordinate of lower-right corner int nBottomRect, // y-coordinate of lower-right corner int nWidthEllipse, // height of ellipse int nHeightEllipse // width of ellipse ); #endregion #region Public Members /// 'true' if Dark Mode Color is set in Windows's Settings. public bool IsDarkMode { get; set; } = false; /// Option to re-colorize all Icons in Toolbars and Menus. public bool ColorizeIcons { get; set; } = true; /// Option to make all Panels Borders Rounded public bool RoundedPanels { get; set; } = false; /// The PArent form for them all. public Form OwnerForm { get; set; } /// Windows Colors. Can be customized. public OSThemeColors OScolors { get; set; } #endregion #region Constructors /// This tries to automatically apply Windows Dark Mode (if enabled) to a Form. /// The Form to become Dark public DarkModeCS(Form _Form) { OwnerForm = _Form; IsDarkMode = GetWindowsColorMode() <= 0 ? true : false; OScolors = GetSystemColors(OwnerForm); if (IsDarkMode && OScolors != null) { if (OwnerForm != null && OwnerForm.Controls != null) { foreach (System.Windows.Forms.Control _control in OwnerForm.Controls) { ThemeControl(_control); } OwnerForm.ControlAdded += (object sender, ControlEventArgs e) => { ThemeControl(e.Control); }; } } } /// This tries to automatically apply Windows Dark Mode (if enabled) to a Form. /// The Form to become Dark /// [OPTIONAL] re-colorize all Icons in Toolbars and Menus. /// [OPTIONAL] make all Panels Borders Rounded public DarkModeCS(Form _Form, bool _ColorizeIcons = true, bool _RoundedPanels = false) { //Sets the Properties: OwnerForm = _Form; ColorizeIcons = _ColorizeIcons; RoundedPanels = _RoundedPanels; IsDarkMode = GetWindowsColorMode() <= 0 ? true : false; OScolors = GetSystemColors(OwnerForm); if (IsDarkMode && OScolors != null) { if (OwnerForm != null && OwnerForm.Controls != null) { foreach (System.Windows.Forms.Control _control in OwnerForm.Controls) { ThemeControl(_control); } OwnerForm.ControlAdded += (object sender, ControlEventArgs e) => { ThemeControl(e.Control); }; } } } #endregion #region Public Methods /// Recursively apply the Colors from 'OScolors' to the Control and all its childs. /// Can be a Form or any Winforms Control. public void ThemeControl(System.Windows.Forms.Control control) { BorderStyle BStyle = (IsDarkMode ? BorderStyle.FixedSingle : BorderStyle.Fixed3D); FlatStyle FStyle = (IsDarkMode ? FlatStyle.Flat : FlatStyle.Standard); //Change the Colors only if its the default ones, this allows the user to set own colors: if (control.BackColor == SystemColors.Control || control.BackColor == SystemColors.Window) { control.GetType().GetProperty("BackColor")?.SetValue(control, OScolors.Control); } if (control.ForeColor == SystemColors.ControlText || control.ForeColor == SystemColors.WindowText) { control.GetType().GetProperty("ForeColor")?.SetValue(control, OScolors.TextActive); } control.GetType().GetProperty("BorderStyle")?.SetValue(control, BStyle); control.HandleCreated += (object sender, EventArgs e) => { ApplySystemDarkTheme(control); }; control.ControlAdded += (object sender, ControlEventArgs e) => { ThemeControl(e.Control); }; if (control is TextBox tb) { //SetRoundBorders(tb, 4, OScolors.SurfaceDark, 1); } if (control is Panel panel) { // Process the panel within the container panel.BackColor = OScolors.Surface; panel.BorderStyle = BorderStyle.None; if (!(panel.Parent is TabControl) || !(panel.Parent is TableLayoutPanel)) { if (RoundedPanels) { SetRoundBorders(panel, 6, OScolors.SurfaceDark, 1); } } } if (control is GroupBox group) { group.BackColor = group.Parent.BackColor; group.ForeColor = OScolors.TextInactive; } if (control is TableLayoutPanel table) { // Process the panel within the container table.BackColor = table.Parent.BackColor; table.BorderStyle = BorderStyle.None; } if (control is TabControl tab) { tab.Appearance = TabAppearance.Normal; tab.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; tab.DrawItem += (object sender, DrawItemEventArgs e) => { //Draw the background of the main control using (SolidBrush backColor = new SolidBrush(tab.Parent.BackColor)) { e.Graphics.FillRectangle(backColor, tab.ClientRectangle); } using (Brush tabBack = new SolidBrush(OScolors.Surface)) { for (int i = 0; i < tab.TabPages.Count; i++) { TabPage tabPage = tab.TabPages[i]; tabPage.BackColor = OScolors.Surface; tabPage.BorderStyle = BorderStyle.FixedSingle; tabPage.ControlAdded += (object _s, ControlEventArgs _e) => { ThemeControl(_e.Control); }; var tBounds = e.Bounds; //tBounds.Inflate(100, 100); bool IsSelected = (tab.SelectedIndex == i); if (IsSelected) { e.Graphics.FillRectangle(tabBack, tBounds); TextRenderer.DrawText(e.Graphics, tabPage.Text, tabPage.Font, e.Bounds, OScolors.TextActive); } else { TextRenderer.DrawText(e.Graphics, tabPage.Text, tabPage.Font, tab.GetTabRect(i), OScolors.TextInactive); } } } }; } if (control is FlatTabControl fTab) { fTab.BackColor = OScolors.Background; fTab.TabColor = OScolors.Surface; fTab.SelectTabColor = OScolors.Control; fTab.SelectedForeColor = OScolors.TextActive; fTab.BorderColor = OScolors.Background; fTab.ForeColor = OScolors.TextInactive; fTab.LineColor = OScolors.Background; } if (control is PictureBox pic) { pic.BorderStyle = BorderStyle.None; pic.BackColor = pic.Parent.BackColor; } if (control is Button button) { button.FlatStyle = FStyle; button.FlatAppearance.CheckedBackColor = OScolors.Accent; button.BackColor = OScolors.Control; button.FlatAppearance.BorderColor = (OwnerForm.AcceptButton == button) ? OScolors.Accent : OScolors.Control; SetRoundBorders(button, 4, OScolors.SurfaceDark, 1); button.Paint += (object sender, PaintEventArgs e) => { if (sender is Button paintButton && !paintButton.Enabled) { TextRenderer.DrawText(e.Graphics, paintButton.Text, paintButton.Font, paintButton.ClientRectangle, Color.Gray); } }; } if (control is Label label) { label.BorderStyle = BorderStyle.None; } if (control is LinkLabel link) { link.LinkColor = OScolors.AccentLight; link.VisitedLinkColor = OScolors.Primary; } if (control is CheckBox chk) { chk.BackColor = chk.Parent.BackColor; } if (control is RadioButton opt) { opt.BackColor = opt.Parent.BackColor; } if (control is ComboBox combo) { combo.FlatStyle = FStyle; combo.BackColor = OScolors.Control; control.GetType().GetProperty("ButtonColor")?.SetValue(control, OScolors.Surface); combo.Invalidate(); } if (control is MenuStrip menu) { menu.RenderMode = ToolStripRenderMode.Professional; menu.Renderer = new MyRenderer(new CustomColorTable(OScolors), ColorizeIcons) { MyColors = OScolors }; } if (control is ToolStrip toolBar) { toolBar.GripStyle = ToolStripGripStyle.Hidden; toolBar.RenderMode = ToolStripRenderMode.Professional; toolBar.Renderer = new MyRenderer(new CustomColorTable(OScolors), ColorizeIcons) { MyColors = OScolors }; } if (control is ContextMenuStrip cMenu) { cMenu.RenderMode = ToolStripRenderMode.Professional; cMenu.Renderer = new MyRenderer(new CustomColorTable(OScolors), ColorizeIcons) { MyColors = OScolors }; } if (control is DataGridView grid) { grid.EnableHeadersVisualStyles = false; grid.BorderStyle = BorderStyle.FixedSingle; grid.BackgroundColor = OScolors.Control; grid.GridColor = OScolors.Control; grid.DefaultCellStyle.BackColor = OScolors.Surface; grid.DefaultCellStyle.ForeColor = OScolors.TextActive; grid.ColumnHeadersDefaultCellStyle.BackColor = OScolors.Surface; grid.ColumnHeadersDefaultCellStyle.ForeColor = OScolors.TextActive; grid.ColumnHeadersDefaultCellStyle.SelectionBackColor = OScolors.Accent; grid.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single; grid.ColumnHeadersHeight = 140; grid.RowHeadersDefaultCellStyle.BackColor = OScolors.Surface; grid.RowHeadersDefaultCellStyle.ForeColor = OScolors.TextActive; grid.RowHeadersDefaultCellStyle.SelectionBackColor = OScolors.Accent; grid.RowHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single; } if (control is PropertyGrid pGrid) { pGrid.BackColor = OScolors.Control; pGrid.ViewBackColor = OScolors.Control; pGrid.LineColor = OScolors.Surface; pGrid.ViewForeColor = OScolors.TextActive; pGrid.ViewBorderColor = OScolors.ControlDark; pGrid.CategoryForeColor = OScolors.TextActive; pGrid.CategorySplitterColor = OScolors.ControlLight; } if (control is TreeView tree) { tree.BorderStyle = BorderStyle.None; tree.BackColor = OScolors.Surface; } if (control.ContextMenuStrip != null) ThemeControl(control.ContextMenuStrip); foreach (System.Windows.Forms.Control childControl in control.Controls) { // Recursively process its children ThemeControl(childControl); } } /// Returns Windows Color Mode for Applications. /// 0=dark theme, 1=light theme /// public static int GetWindowsColorMode(bool GetSystemColorModeInstead = false) { try { return GetThemeColorScheme() ? 1 : 0; } catch { return 1; } } private static bool GetThemeColorScheme() { var currentPath = Directory.GetCurrentDirectory(); var currentCombine = Path.Combine(currentPath, "Aurora.exe"); var config = ConfigurationManager.OpenExeConfiguration(currentCombine); var theme_color = config.AppSettings.Settings["THEME_COLOR"]; return theme_color == null || theme_color.Value.StartsWith("light", StringComparison.InvariantCultureIgnoreCase); } /// Returns the Accent Color used by Windows. /// a Color public static Color GetWindowsAccentColor() { DWMCOLORIZATIONcolors colors = new DWMCOLORIZATIONcolors(); DwmGetColorizationParameters(ref colors); //get the theme --> only if Windows 10 or newer if (IsWindows10orGreater()) { var opaque = true; var color = (int)colors.ColorizationColor; return Color.FromArgb((byte)(opaque ? 255 : (color >> 24) & 0xff), (byte)((color >> 16) & 0xff), (byte)((color >> 8) & 0xff), (byte)(color) & 0xff); } else { return Color.CadetBlue; } } /// Returns Windows's System Colors for UI components following Google Material Design concepts. /// [OPTIONAL] Applies DarkMode (if set) to this Window Title and Background. /// List of Colors: Background, OnBackground, Surface, OnSurface, Primary, OnPrimary, Secondary, OnSecondary public static OSThemeColors GetSystemColors(Form Window = null) { OSThemeColors _ret = new OSThemeColors(); bool IsDarkMode = (GetWindowsColorMode() <= 0); //<- O: DarkMode, 1: LightMode if (IsDarkMode) { _ret.Background = Color.FromArgb(32, 32, 32); //<- Negro Claro _ret.BackgroundDark = ControlPaint.Dark(_ret.Background); _ret.BackgroundLight = ControlPaint.Light(_ret.Background); _ret.Surface = Color.FromArgb(43, 43, 43); //<- Gris Oscuro _ret.SurfaceLight = ControlPaint.Light(_ret.Surface); _ret.SurfaceDark = ControlPaint.Dark(_ret.Surface); _ret.TextActive = Color.White; _ret.TextInactive = ControlPaint.Light(_ret.TextActive); _ret.TextInAccent = GetReadableColor(_ret.Accent); _ret.Control = Color.FromArgb(55, 55, 55); //<- Gris Oscuro _ret.ControlDark = ControlPaint.Dark(_ret.Control); _ret.ControlLight = ControlPaint.Light(_ret.Control); _ret.Primary = Color.FromArgb(3, 218, 198); //<- Verde Pastel _ret.Secondary = Color.MediumSlateBlue; //<- Magenta Claro //Apply Window's Dark Mode to the Form's Title bar if (Window != null) { //SetWin32ApiTheme(Window); ApplySystemDarkTheme(Window); Window.BackColor = _ret.Background; Window.ForeColor = _ret.TextActive; } } return _ret; } /// Apply Round Corners to the indicated Control or Form. /// the one who will have rounded Corners. Set BorderStyle = None. /// Radious for the Corners /// Color for the Border /// Size in pixels of the border line /// public static void SetRoundBorders(System.Windows.Forms.Control _Control, int Radius = 10, Color? borderColor = null, int borderSize = 2, bool underlinedStyle = false) { try { borderColor = borderColor ?? Color.MediumSlateBlue; if (_Control != null) { _Control.GetType().GetProperty("BorderStyle")?.SetValue(_Control, BorderStyle.None); _Control.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, _Control.Width, _Control.Height, Radius, Radius)); _Control.Paint += (object sender, PaintEventArgs e) => { //base.OnPaint(e); Graphics graph = e.Graphics; if (Radius > 1)//Rounded TextBox { //-Fields var rectBorderSmooth = _Control.ClientRectangle; var rectBorder = Rectangle.Inflate(rectBorderSmooth, -borderSize, -borderSize); int smoothSize = borderSize > 0 ? borderSize : 1; using (GraphicsPath pathBorderSmooth = GetFigurePath(rectBorderSmooth, Radius)) using (GraphicsPath pathBorder = GetFigurePath(rectBorder, Radius - borderSize)) using (Pen penBorderSmooth = new Pen(_Control.Parent.BackColor, smoothSize)) using (Pen penBorder = new Pen((Color)borderColor, borderSize)) { //-Drawing _Control.Region = new Region(pathBorderSmooth);//Set the rounded region of UserControl if (Radius > 15) //Set the rounded region of TextBox component { using (GraphicsPath pathTxt = GetFigurePath(_Control.ClientRectangle, borderSize * 2)) { _Control.Region = new Region(pathTxt); } } graph.SmoothingMode = SmoothingMode.AntiAlias; penBorder.Alignment = System.Drawing.Drawing2D.PenAlignment.Center; //if (isFocused) penBorder.Color = borderFocusColor; if (underlinedStyle) //Line Style { //Draw border smoothing graph.DrawPath(penBorderSmooth, pathBorderSmooth); //Draw border graph.SmoothingMode = SmoothingMode.None; graph.DrawLine(penBorder, 0, _Control.Height - 1, _Control.Width, _Control.Height - 1); } else //Normal Style { //Draw border smoothing graph.DrawPath(penBorderSmooth, pathBorderSmooth); //Draw border graph.DrawPath(penBorder, pathBorder); } } } }; } } catch { throw; } } #endregion #region Private Methods /// Attemps to apply Window's Dark Style to the Control and all its childs. /// private static void ApplySystemDarkTheme(System.Windows.Forms.Control control = null) { /* DWMWA_USE_IMMERSIVE_DARK_MODE: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute Use with DwmSetWindowAttribute. Allows the window frame for this window to be drawn in dark mode colors when the dark mode system setting is enabled. For compatibility reasons, all windows default to light mode regardless of the system setting. The pvAttribute parameter points to a value of type BOOL. TRUE to honor dark mode for the window, FALSE to always use light mode. This value is supported starting with Windows 11 Build 22000. SetWindowTheme: https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-setwindowtheme Causes a window to use a different set of visual style information than its class normally uses. */ int[] DarkModeOn = new[] { 0x01 }; //<- 1=True, 0=False SetWindowTheme(control.Handle, "DarkMode_Explorer", null); if (DwmSetWindowAttribute(control.Handle, (int)DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, DarkModeOn, 4) != 0) DwmSetWindowAttribute(control.Handle, (int)DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, DarkModeOn, 4); foreach (System.Windows.Forms.Control child in control.Controls) { if (child.Controls.Count != 0) ApplySystemDarkTheme(child); } } private static bool IsWindows10orGreater() { if (WindowsVersion() >= 10) return true; else return false; } private static int WindowsVersion() { //for .Net4.8 and Minor int result = 10; var reg = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); string[] productName = reg.GetValue("ProductName").ToString().Split((char)32); int.TryParse(productName[1], out result); return result; //fixed .Net6 //return System.Environment.OSVersion.Version.Major; } private static Color GetReadableColor(Color backgroundColor) { // Calculate the relative luminance of the background color. // Normalize values to 0-1 range first. double normalizedR = backgroundColor.R / 255.0; double normalizedG = backgroundColor.G / 255.0; double normalizedB = backgroundColor.B / 255.0; double luminance = 0.299 * normalizedR + 0.587 * normalizedG + 0.114 * normalizedB; // Choose a contrasting foreground color based on the luminance, // with a slight bias towards lighter colors for better readability. return luminance < 0.5 ? Color.FromArgb(182, 180, 215) : Color.FromArgb(34, 34, 34); // Dark gray for light backgrounds } // For Rounded Corners: private static GraphicsPath GetFigurePath(Rectangle rect, int radius) { GraphicsPath path = new GraphicsPath(); float curveSize = radius * 2F; path.StartFigure(); path.AddArc(rect.X, rect.Y, curveSize, curveSize, 180, 90); path.AddArc(rect.Right - curveSize, rect.Y, curveSize, curveSize, 270, 90); path.AddArc(rect.Right - curveSize, rect.Bottom - curveSize, curveSize, curveSize, 0, 90); path.AddArc(rect.X, rect.Bottom - curveSize, curveSize, curveSize, 90, 90); path.CloseFigure(); return path; } #endregion } /// Windows 10+ System Colors for Clear Color Mode. public class OSThemeColors { public OSThemeColors() { } /// For the very back of the Window public System.Drawing.Color Background { get; set; } = SystemColors.Control; /// For Borders around the Background public System.Drawing.Color BackgroundDark { get; set; } = SystemColors.ControlDark; /// For hightlights over the Background public System.Drawing.Color BackgroundLight { get; set; } = SystemColors.ControlLight; /// For Container above the Background public System.Drawing.Color Surface { get; set; } = SystemColors.ControlLightLight; /// For Borders around the Surface public System.Drawing.Color SurfaceDark { get; set; } = SystemColors.ControlLight; /// For Highligh over the Surface public System.Drawing.Color SurfaceLight { get; set; } = Color.White; /// For Main Texts public System.Drawing.Color TextActive { get; set; } = SystemColors.ControlText; /// For Inactive Texts public System.Drawing.Color TextInactive { get; set; } = SystemColors.GrayText; /// For Hightligh Texts public System.Drawing.Color TextInAccent { get; set; } = SystemColors.HighlightText; /// For the background of any Control public System.Drawing.Color Control { get; set; } = SystemColors.ButtonFace; /// For Bordes of any Control public System.Drawing.Color ControlDark { get; set; } = SystemColors.ButtonShadow; /// For Highlight elements in a Control public System.Drawing.Color ControlLight { get; set; } = SystemColors.ButtonHighlight; /// Windows 10+ Chosen Accent Color public System.Drawing.Color Accent { get; set; } = DarkModeCS.GetWindowsAccentColor(); public System.Drawing.Color AccentDark { get { return ControlPaint.Dark(Accent); } } public System.Drawing.Color AccentLight { get { return ControlPaint.Light(Accent); } } /// the color displayed most frequently across your app's screens and components. public System.Drawing.Color Primary { get; set; } = SystemColors.Highlight; public System.Drawing.Color PrimaryDark { get { return ControlPaint.Dark(Primary); } } public System.Drawing.Color PrimaryLight { get { return ControlPaint.Light(Primary); } } /// to accent select parts of your UI. public System.Drawing.Color Secondary { get; set; } = SystemColors.HotTrack; public System.Drawing.Color SecondaryDark { get { return ControlPaint.Dark(Secondary); } } public System.Drawing.Color SecondaryLight { get { return ControlPaint.Light(Secondary); } } } /* Custom Renderers for Menus and ToolBars */ public class MyRenderer : ToolStripProfessionalRenderer { public bool ColorizeIcons { get; set; } = true; public OSThemeColors MyColors { get; set; } //<- Your Custom Colors Colection public MyRenderer(ProfessionalColorTable table, bool pColorizeIcons = true) : base(table) { ColorizeIcons = pColorizeIcons; } private void DrawTitleBar(Graphics g, Rectangle rect) { // Assign the image for the grip. //Image titlebarGrip = titleBarGripBmp; // Fill the titlebar. // This produces the gradient and the rounded-corner effect. //g.DrawLine(new Pen(titlebarColor1), rect.X, rect.Y, rect.X + rect.Width, rect.Y); //g.DrawLine(new Pen(titlebarColor2), rect.X, rect.Y + 1, rect.X + rect.Width, rect.Y + 1); //g.DrawLine(new Pen(titlebarColor3), rect.X, rect.Y + 2, rect.X + rect.Width, rect.Y + 2); //g.DrawLine(new Pen(titlebarColor4), rect.X, rect.Y + 3, rect.X + rect.Width, rect.Y + 3); //g.DrawLine(new Pen(titlebarColor5), rect.X, rect.Y + 4, rect.X + rect.Width, rect.Y + 4); //g.DrawLine(new Pen(titlebarColor6), rect.X, rect.Y + 5, rect.X + rect.Width, rect.Y + 5); //g.DrawLine(new Pen(titlebarColor7), rect.X, rect.Y + 6, rect.X + rect.Width, rect.Y + 6); // Center the titlebar grip. //g.DrawImage( // titlebarGrip, // new Point(rect.X + ((rect.Width / 2) - (titlebarGrip.Width / 2)), // rect.Y + 1)); } // This method handles the RenderGrip event. protected override void OnRenderGrip(ToolStripGripRenderEventArgs e) { DrawTitleBar( e.Graphics, new Rectangle(0, 0, e.ToolStrip.Width, 7)); } // This method handles the RenderToolStripBorder event. protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { DrawTitleBar( e.Graphics, new Rectangle(0, 0, e.ToolStrip.Width, 7)); } // Background of the whole ToolBar Or MenuBar: protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { e.ToolStrip.BackColor = MyColors.Background; base.OnRenderToolStripBackground(e); } // For Normal Buttons on a ToolBar: protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) { Graphics g = e.Graphics; Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); Color gradientBegin = MyColors.Background; // Color.FromArgb(203, 225, 252); Color gradientEnd = MyColors.Background; Pen BordersPencil = new Pen(MyColors.Background); ToolStripButton button = e.Item as ToolStripButton; if (button.Pressed || button.Checked) { gradientBegin = MyColors.Control; gradientEnd = MyColors.Control; } else if (button.Selected) { gradientBegin = MyColors.Accent; gradientEnd = MyColors.Accent; } if (button.Name == "toolStripButtonSnapMode") { gradientBegin = button.BackColor; gradientEnd = button.BackColor; BordersPencil.Dispose(); BordersPencil = new Pen(Color.FromArgb(0xA0, Color.Gray)); } using (Brush b = new LinearGradientBrush( bounds, gradientBegin, gradientEnd, LinearGradientMode.Vertical)) { g.FillRectangle(b, bounds); } Size outSize = new Size(bounds.Width - 1, bounds.Height - 1); Rectangle outBounds = new Rectangle(Point.Empty, outSize); e.Graphics.DrawRectangle( BordersPencil, outBounds); BordersPencil.Dispose(); } // For DropDown Buttons on a ToolBar: protected override void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e) { Graphics g = e.Graphics; Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); Color gradientBegin = MyColors.Background; // Color.FromArgb(203, 225, 252); Color gradientEnd = MyColors.Background; Pen BordersPencil = new Pen(MyColors.Background); //1. Determine the colors to use: if (e.Item.Pressed) { gradientBegin = MyColors.Control; gradientEnd = MyColors.Control; } else if (e.Item.Selected) { gradientBegin = MyColors.Accent; gradientEnd = MyColors.Accent; } //2. Draw the Box around the Control using (Brush b = new LinearGradientBrush( bounds, gradientBegin, gradientEnd, LinearGradientMode.Vertical)) { e.Graphics.FillRectangle(b, bounds); } //3. Draws the Chevron: #region Chevron //int Padding = 2; //<- From the right side //Size cSize = new Size(8, 4); //<- Size of the Chevron: 8x4 px //Pen ChevronPen = new Pen(MyColors.TextInactive, 2); //<- Color and Border Width //Point P1 = new Point(bounds.Width - (cSize.Width + Padding), (bounds.Height / 2) - (cSize.Height / 2)); //Point P2 = new Point(bounds.Width - Padding, (bounds.Height / 2) - (cSize.Height / 2)); //Point P3 = new Point(bounds.Width - (cSize.Width / 2 + Padding), (bounds.Height / 2) + (cSize.Height / 2)); //e.Graphics.DrawLine(ChevronPen, P1, P3); //e.Graphics.DrawLine(ChevronPen, P2, P3); #endregion } // For SplitButtons on a ToolBar: protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) { Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); Color gradientBegin = MyColors.Background; // Color.FromArgb(203, 225, 252); Color gradientEnd = MyColors.Background; //1. Determine the colors to use: if (e.Item.Pressed) { gradientBegin = MyColors.Control; gradientEnd = MyColors.Control; } else if (e.Item.Selected) { gradientBegin = MyColors.Accent; gradientEnd = MyColors.Accent; } //2. Draw the Box around the Control using (Brush b = new LinearGradientBrush( bounds, gradientBegin, gradientEnd, LinearGradientMode.Vertical)) { e.Graphics.FillRectangle(b, bounds); } //3. Draws the Chevron: #region Chevron int Padding = 2; //<- From the right side Size cSize = new Size(8, 4); //<- Size of the Chevron: 8x4 px Pen ChevronPen = new Pen(MyColors.TextInactive, 2); //<- Color and Border Width Point P1 = new Point(bounds.Width - (cSize.Width + Padding), (bounds.Height / 2) - (cSize.Height / 2)); Point P2 = new Point(bounds.Width - Padding, (bounds.Height / 2) - (cSize.Height / 2)); Point P3 = new Point(bounds.Width - (cSize.Width / 2 + Padding), (bounds.Height / 2) + (cSize.Height / 2)); e.Graphics.DrawLine(ChevronPen, P1, P3); e.Graphics.DrawLine(ChevronPen, P2, P3); #endregion } // For the Text Color of all Items: protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { if (e.Item.Enabled) { e.TextColor = MyColors.TextActive; } else { e.TextColor = MyColors.TextInactive; } base.OnRenderItemText(e); } protected override void OnRenderItemBackground(ToolStripItemRenderEventArgs e) { base.OnRenderItemBackground(e); // Only draw border for ToolStripItem items if (e.Item is ToolStripItem) { Rectangle rect = new Rectangle(Point.Empty, e.Item.Size); e.Graphics.DrawRectangle(new Pen(MyColors.ControlLight, 1), rect); } } // For Menu Items BackColor: protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { Graphics g = e.Graphics; Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); Color gradientBegin = MyColors.Background; // Color.FromArgb(203, 225, 252); Color gradientEnd = MyColors.Background; // Color.FromArgb(125, 165, 224); bool DrawIt = false; var _menu = e.Item as ToolStripItem; if (_menu.Pressed) { gradientBegin = MyColors.Control; // Color.FromArgb(254, 128, 62); gradientEnd = MyColors.Control; // Color.FromArgb(255, 223, 154); DrawIt = true; } else if (_menu.Selected) { gradientBegin = MyColors.Accent;// Color.FromArgb(255, 255, 222); gradientEnd = MyColors.Accent; // Color.FromArgb(255, 203, 136); DrawIt = true; } if (DrawIt) { using (Brush b = new LinearGradientBrush( bounds, gradientBegin, gradientEnd, LinearGradientMode.Vertical)) { g.FillRectangle(b, bounds); } } } // Re-Colors the Icon Images to a Clear color: protected override void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) { if (ColorizeIcons && e.Image != null) { // Get the current icon Image image = e.Image; Color _ClearColor = e.Item.Enabled ? MyColors.TextInactive : MyColors.SurfaceDark; // Create a new image with the desired color adjustments using (Image adjustedImage = ChangeToColor(image, _ClearColor)) { e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; e.Graphics.CompositingQuality = CompositingQuality.HighQuality; e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.DrawImage(adjustedImage, e.ImageRectangle); } } } /// Colorea una imagen usando una Matrix de Color. /// Imagen a Colorear /// Color a Utilizar private Bitmap ChangeToColor(Bitmap bmp, Color c) { Bitmap bmp2 = new Bitmap(bmp.Width, bmp.Height); using (Graphics g = Graphics.FromImage(bmp2)) { g.InterpolationMode = InterpolationMode.HighQualityBilinear; g.CompositingQuality = CompositingQuality.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality; System.Drawing.Imaging.ColorMatrix colorMatrix = new System.Drawing.Imaging.ColorMatrix(new float[][] { new float[]{1.0f, 0, 0, 0, 0}, // red scaling factor new float[]{ 0, 1.0f, 0, 0, 0}, // green scaling factor new float[]{ 0, 0, 1.0f, 0, 0}, // blue scaling factor new float[]{ 0, 0, 0, 1.0f, 0}, // alpha scaling factor new float[]{ 0, 0, 0, 0, 1.0f}, // three translations } ); System.Drawing.Imaging.ImageAttributes attributes = new System.Drawing.Imaging.ImageAttributes(); attributes.SetColorMatrix(colorMatrix); g.DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, attributes); } return bmp2; } private Image ChangeToColor(Image bmp, Color c) => (Image)ChangeToColor((Bitmap)bmp, c); } public class CustomColorTable : ProfessionalColorTable { public OSThemeColors Colors { get; set; } public CustomColorTable(OSThemeColors _Colors) { Colors = _Colors; base.UseSystemColors = false; } public override Color ImageMarginGradientBegin { get { return Colors.Control; } } public override Color ImageMarginGradientMiddle { get { return Colors.Control; } } public override Color ImageMarginGradientEnd { get { return Colors.Control; } } } }