From d495460f5e3bf05403e92e1a6351fa6aaf56052a Mon Sep 17 00:00:00 2001 From: "Leaf Shi (BEYONDSOFT CONSULTING INC)" Date: Wed, 20 Aug 2025 00:33:12 -0700 Subject: [PATCH 1/5] Add judgement "IsHandleCreated" in OwnerDraw --- .../System/Windows/Forms/Controls/Buttons/Button.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs index e76dd755f29..2f9838b16aa 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs @@ -161,12 +161,13 @@ private protected override bool OwnerDraw get { if (Application.IsDarkModeEnabled - // The SystemRenderer cannot render images. So, we flip to our // own DarkMode renderer, if we need to render images, except if... && Image is null // ...or a BackgroundImage, except if... && BackgroundImage is null + // Delay BackgroundImage check until handle is created to avoid early null misjudgment + && IsHandleCreated // ...the user wants to opt out of implicit DarkMode rendering. && DarkModeRequestState is true From 0fee68a54e7486db2e0aaab3c5b7202c42dbd4da Mon Sep 17 00:00:00 2001 From: Leaf Shi Date: Thu, 4 Sep 2025 23:14:36 -0700 Subject: [PATCH 2/5] Invoking UpdateOwnerDraw in BackgroundImage property to update the OwnerDraw flag to ensure correct visual behavior --- src/System.Windows.Forms/PublicAPI.Unshipped.txt | 1 + .../Windows/Forms/Controls/Buttons/Button.cs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 3348c917404..904ff4f1046 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +override System.Windows.Forms.Button.BackgroundImage.set -> void static System.Windows.Forms.Application.SetColorMode(System.Windows.Forms.SystemColorMode systemColorMode) -> void static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs index 2f9838b16aa..4dc7938e6f1 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs @@ -148,6 +148,18 @@ public virtual DialogResult DialogResult } } + public override Image? BackgroundImage + { + set + { + base.BackgroundImage = value; + + // BackgroundImage changes may affect rendering logic, + // so we manually update the OwnerDraw flag to ensure correct visual behavior. + UpdateOwnerDraw(); + } + } + /// /// Defines, whether the control is owner-drawn. Based on this, /// the UserPaint flags get set, which in turn makes it later @@ -166,8 +178,6 @@ private protected override bool OwnerDraw && Image is null // ...or a BackgroundImage, except if... && BackgroundImage is null - // Delay BackgroundImage check until handle is created to avoid early null misjudgment - && IsHandleCreated // ...the user wants to opt out of implicit DarkMode rendering. && DarkModeRequestState is true From 3fa5fe44f43ad785e0571c27982255527e0aa4d5 Mon Sep 17 00:00:00 2001 From: Leaf Shi Date: Sun, 7 Sep 2025 18:46:20 -0700 Subject: [PATCH 3/5] Add judgement "IsDarkModeEnabled" for property BackgroundImage --- .../System/Windows/Forms/Controls/Buttons/Button.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs index 4dc7938e6f1..6eada6bc3d9 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs @@ -154,9 +154,12 @@ public override Image? BackgroundImage { base.BackgroundImage = value; - // BackgroundImage changes may affect rendering logic, - // so we manually update the OwnerDraw flag to ensure correct visual behavior. - UpdateOwnerDraw(); + if (Application.IsDarkModeEnabled) + { + // BackgroundImage changes may affect rendering logic, + // so we manually update the OwnerDraw flag to ensure correct visual behavior. + UpdateOwnerDraw(); + } } } From 9e82a5ff58f078e165296f7a13c1e783f268d219 Mon Sep 17 00:00:00 2001 From: Leaf Shi Date: Thu, 11 Sep 2025 02:59:07 -0700 Subject: [PATCH 4/5] Put DrawButtonBorder after PaintImage and removing the rounded border in Flat mode --- .../Buttons/ButtonInternal/ButtonBaseAdapter.cs | 4 +--- .../DarkMode/ButtonDarkModeRendererBase.cs | 4 ++++ .../DarkMode/FlatButtonDarkModeRenderer.cs | 13 ++----------- .../DarkMode/PopupButtonDarkModeRenderer.cs | 5 +---- .../DarkMode/SystemButtonDarkModeRenderer.cs | 3 +++ 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs index 39b5b0ab852..b2aea2db745 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs @@ -549,15 +549,13 @@ internal void PaintImage(PaintEventArgs e, LayoutData layout) { if (Application.IsDarkModeEnabled && Control.DarkModeRequestState is true && Control.BackgroundImage is not null) { - Rectangle bounds = Control.ClientRectangle; - bounds.Inflate(-ButtonBorderSize, -ButtonBorderSize); ControlPaint.DrawBackgroundImage( e.GraphicsInternal, Control.BackgroundImage, Color.Transparent, Control.BackgroundImageLayout, Control.ClientRectangle, - bounds, + Control.ClientRectangle, Control.DisplayRectangle.Location, Control.RightToLeft); } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs index 9d655a618b0..7bb1e950c26 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs @@ -66,6 +66,8 @@ public void RenderButton( // Paint image and field using the provided delegates paintImage(contentBounds); + DrawButtonBorder(graphics, bounds, state, isDefault); + paintField(); if (focused && showFocusCues) @@ -76,6 +78,8 @@ public void RenderButton( } } + public abstract void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault); + public abstract Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor); public abstract void DrawFocusIndicator(Graphics graphics, Rectangle contentBounds, bool isDefault); diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs index 2541cb1b6ad..5eb8ccee97d 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs @@ -17,8 +17,6 @@ namespace System.Windows.Forms; internal sealed class FlatButtonDarkModeRenderer : ButtonDarkModeRendererBase { private const int FocusIndicatorInflate = -3; - private const int CornerRadius = 6; - private static readonly Size s_corner = new(CornerRadius, CornerRadius); private protected override Padding PaddingCore { get; } = new(0); @@ -28,10 +26,6 @@ public override Rectangle DrawButtonBackground( // fill background using var back = backColor.GetCachedSolidBrushScope(); graphics.FillRectangle(back, bounds); - - // draw border identical to Win32 - DrawButtonBorder(graphics, bounds, state, isDefault); - // return inner content area (border + 1 px system padding) return Rectangle.Inflate(bounds, -3, -3); } @@ -77,7 +71,7 @@ public override Color GetBackgroundColor(PushButtonState state, bool isDefault) _ => DefaultColors.StandardBackColor }; - private static void DrawButtonBorder(Graphics g, Rectangle bounds, PushButtonState state, bool isDefault) + public override void DrawButtonBorder(Graphics g, Rectangle bounds, PushButtonState state, bool isDefault) { g.SmoothingMode = SmoothingMode.AntiAlias; @@ -98,12 +92,9 @@ private static void DrawSingleBorder(Graphics g, Rectangle rect, Color color) { g.SmoothingMode = SmoothingMode.AntiAlias; - using var path = new GraphicsPath(); - path.AddRoundedRectangle(rect, s_corner); - // a 1‑px stroke, aligned *inside*, is exactly what Win32 draws using var pen = new Pen(color) { Alignment = PenAlignment.Inset }; - g.DrawPath(pen, path); + g.DrawRectangle(pen, rect); } private static Color GetBorderColor(PushButtonState state) => diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs index 5970ad57fca..415c144c058 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs @@ -52,9 +52,6 @@ public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle boun using var brush = backColor.GetCachedSolidBrushScope(); graphics.FillPath(brush, path); - // Draw 3D effect borders - DrawButtonBorder(graphics, paddedBounds, state, isDefault); - // Return content bounds (area inside the button for text/image) return contentBounds; } @@ -120,7 +117,7 @@ public override Color GetBackgroundColor(PushButtonState state, bool isDefault) /// /// Draws the 3D effect border for the button. /// - private static void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault) + public override void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault) { // Save original smoothing mode to restore later SmoothingMode originalMode = graphics.SmoothingMode; diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs index 706ccf777cd..5bcf5abb01c 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs @@ -108,6 +108,9 @@ public override Color GetBackgroundColor(PushButtonState state, bool isDefault) _ => DefaultColors.StandardBackColor }; + public override void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault) => + DrawButtonBorder(graphics, bounds, state, isDefault, false); + /// /// Draws the button border based on the current state, using anti-aliasing and an additional inner border. /// From 7740992d8d50ea13cb198f7c3fcefd01c08cf267 Mon Sep 17 00:00:00 2001 From: Leaf Shi Date: Wed, 17 Sep 2025 02:00:27 -0700 Subject: [PATCH 5/5] Fix the border issue --- .../ButtonInternal/ButtonBaseAdapter.cs | 13 ------ .../DarkMode/ButtonDarkModeAdapter.cs | 42 ++++++++++++++++++- .../DarkMode/ButtonDarkModeRendererBase.cs | 12 ++++-- .../DarkMode/FlatButtonDarkModeRenderer.cs | 19 +++++---- .../DarkMode/IButtonRenderer.cs | 5 ++- .../DarkMode/PopupButtonDarkModeRenderer.cs | 6 +-- .../DarkMode/SystemButtonDarkModeRenderer.cs | 4 +- 7 files changed, 69 insertions(+), 32 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs index b2aea2db745..9e97f45b584 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs @@ -547,19 +547,6 @@ internal void PaintField( /// internal void PaintImage(PaintEventArgs e, LayoutData layout) { - if (Application.IsDarkModeEnabled && Control.DarkModeRequestState is true && Control.BackgroundImage is not null) - { - ControlPaint.DrawBackgroundImage( - e.GraphicsInternal, - Control.BackgroundImage, - Color.Transparent, - Control.BackgroundImageLayout, - Control.ClientRectangle, - Control.ClientRectangle, - Control.DisplayRectangle.Location, - Control.RightToLeft); - } - if (Control.Image is not null) { // Setup new clip region & draw diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeAdapter.cs index b3b0f8d2e3b..908ad33220d 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeAdapter.cs @@ -70,6 +70,32 @@ private Color GetButtonBackColor(PushButtonState state) return textColor; } + internal void PaintBackgroundImage(PaintEventArgs e, int borderSize, Rectangle paddedBounds) + { + if (Application.IsDarkModeEnabled && Control.DarkModeRequestState is true && Control.BackgroundImage is not null) + { + Rectangle bounds = paddedBounds; + if (Control.FlatStyle == FlatStyle.Popup) + { + bounds.Inflate(-1, -1); + } + else + { + bounds.Inflate(-1 - borderSize, -1 - borderSize); + } + + ControlPaint.DrawBackgroundImage( + e.GraphicsInternal, + Control.BackgroundImage, + Color.Transparent, + Control.BackgroundImageLayout, + paddedBounds, + bounds, + bounds.Location, + Control.RightToLeft); + } + } + internal override void PaintUp(PaintEventArgs e, CheckState state) { try @@ -86,6 +112,7 @@ internal override void PaintUp(PaintEventArgs e, CheckState state) g, Control.ClientRectangle, Control.FlatStyle, + Control.FlatAppearance.BorderSize, pushButtonState, Control.IsDefault, Control.Focused, @@ -93,6 +120,10 @@ internal override void PaintUp(PaintEventArgs e, CheckState state) Control.Parent?.BackColor ?? Control.BackColor, GetButtonBackColor(pushButtonState), _ => PaintImage(e, layout), + _ => PaintBackgroundImage( + e, + Control.FlatAppearance.BorderSize, + Control.ClientRectangle), () => PaintField( e, layout, @@ -124,6 +155,7 @@ internal override void PaintDown(PaintEventArgs e, CheckState state) g, Control.ClientRectangle, Control.FlatStyle, + Control.FlatAppearance.BorderSize, PushButtonState.Pressed, Control.IsDefault, Control.Focused, @@ -131,6 +163,10 @@ internal override void PaintDown(PaintEventArgs e, CheckState state) Control.Parent?.BackColor ?? Control.BackColor, GetButtonBackColor(PushButtonState.Pressed), _ => PaintImage(e, layout), + _ => PaintBackgroundImage( + e, + Control.FlatAppearance.BorderSize, + Control.ClientRectangle), () => PaintField( e, layout, @@ -156,12 +192,12 @@ internal override void PaintOver(PaintEventArgs e, CheckState state) var g = e.GraphicsInternal; var smoothingMode = g.SmoothingMode; g.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias; - LayoutData layout = CommonLayout().Layout(); ButtonDarkModeRenderer.RenderButton( g, Control.ClientRectangle, Control.FlatStyle, + Control.FlatAppearance.BorderSize, PushButtonState.Hot, Control.IsDefault, Control.Focused, @@ -169,6 +205,10 @@ internal override void PaintOver(PaintEventArgs e, CheckState state) Control.Parent?.BackColor ?? Control.BackColor, GetButtonBackColor(PushButtonState.Hot), _ => PaintImage(e, layout), + _ => PaintBackgroundImage( + e, + Control.FlatAppearance.BorderSize, + Control.ClientRectangle), () => PaintField( e, layout, diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs index 7bb1e950c26..a177f760a79 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs @@ -32,6 +32,7 @@ public void RenderButton( Graphics graphics, Rectangle bounds, FlatStyle flatStyle, + int borderSize, PushButtonState state, bool isDefault, bool focused, @@ -39,6 +40,7 @@ public void RenderButton( Color parentBackgroundColor, Color backColor, Action paintImage, + Action paintBackgroundImage, Action paintField) { ArgumentNullException.ThrowIfNull(graphics); @@ -61,12 +63,14 @@ public void RenderButton( height: bounds.Height - padding.Vertical); // Draw button background and get content bounds - Rectangle contentBounds = DrawButtonBackground(graphics, paddedBounds, state, isDefault, backColor); + Rectangle contentBounds = DrawButtonBackground(graphics, paddedBounds, borderSize, state, isDefault, backColor); + + paintBackgroundImage(paddedBounds); // Paint image and field using the provided delegates paintImage(contentBounds); - DrawButtonBorder(graphics, bounds, state, isDefault); + DrawButtonBorder(graphics, bounds, borderSize, state, isDefault); paintField(); @@ -78,9 +82,9 @@ public void RenderButton( } } - public abstract void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault); + public abstract void DrawButtonBorder(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault); - public abstract Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor); + public abstract Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault, Color backColor); public abstract void DrawFocusIndicator(Graphics graphics, Rectangle contentBounds, bool isDefault); diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs index 5eb8ccee97d..33708cfa4e6 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs @@ -21,11 +21,15 @@ internal sealed class FlatButtonDarkModeRenderer : ButtonDarkModeRendererBase private protected override Padding PaddingCore { get; } = new(0); public override Rectangle DrawButtonBackground( - Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor) + Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault, Color backColor) { // fill background using var back = backColor.GetCachedSolidBrushScope(); - graphics.FillRectangle(back, bounds); + + Rectangle rectangle = bounds; + rectangle.Inflate(-1 - borderSize, -1 - borderSize); + + graphics.FillRectangle(back, rectangle); // return inner content area (border + 1 px system padding) return Rectangle.Inflate(bounds, -3, -3); } @@ -71,29 +75,28 @@ public override Color GetBackgroundColor(PushButtonState state, bool isDefault) _ => DefaultColors.StandardBackColor }; - public override void DrawButtonBorder(Graphics g, Rectangle bounds, PushButtonState state, bool isDefault) + public override void DrawButtonBorder(Graphics g, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault) { g.SmoothingMode = SmoothingMode.AntiAlias; // Win32 draws its stroke fully *inside* the control → inset by 1 px Rectangle outer = Rectangle.Inflate(bounds, -1, -1); - DrawSingleBorder(g, outer, GetBorderColor(state)); + DrawSingleBorder(g, outer, borderSize, GetBorderColor(state)); // Default button gets a second 1‑px border one pixel further inside if (isDefault) { Rectangle inner = Rectangle.Inflate(outer, -1, -1); - DrawSingleBorder(g, inner, DefaultColors.AcceptFocusIndicatorBackColor); + DrawSingleBorder(g, inner, borderSize, DefaultColors.AcceptFocusIndicatorBackColor); } } - private static void DrawSingleBorder(Graphics g, Rectangle rect, Color color) + private static void DrawSingleBorder(Graphics g, Rectangle rect, int borderSize, Color color) { g.SmoothingMode = SmoothingMode.AntiAlias; - // a 1‑px stroke, aligned *inside*, is exactly what Win32 draws - using var pen = new Pen(color) { Alignment = PenAlignment.Inset }; + using var pen = new Pen(color, borderSize) { Alignment = PenAlignment.Inset }; g.DrawRectangle(pen, rect); } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/IButtonRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/IButtonRenderer.cs index ab1f7844042..d465b89d6a7 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/IButtonRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/IButtonRenderer.cs @@ -43,6 +43,7 @@ void RenderButton( Graphics graphics, Rectangle bounds, FlatStyle flatStyle, + int BorderSize, PushButtonState state, bool isDefault, bool focused, @@ -50,6 +51,7 @@ void RenderButton( Color parentBackgroundColor, Color backColor, Action paintImage, + Action paintBackgroundImage, Action paintField); /// @@ -57,10 +59,11 @@ void RenderButton( /// /// Graphics context to draw on /// Bounds of the button + /// BorderSize of the button /// State of the button (normal, hot, pressed, disabled) /// True if button is the default button /// The content bounds (area inside the button for text/image) - Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor); + Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault, Color backColor); /// /// Draws focus indicator appropriate for this style. diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs index 415c144c058..b3b255f0b5e 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs @@ -30,11 +30,11 @@ internal class PopupButtonDarkModeRenderer : ButtonDarkModeRendererBase /// /// Draws button background with popup styling, including subtle 3D effect. /// - public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor) + public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault, Color backColor) { // Use padding from ButtonDarkModeRenderer Padding padding = PaddingCore; - Rectangle paddedBounds = Rectangle.Inflate(bounds, -padding.Left, -padding.Top); + Rectangle paddedBounds = Rectangle.Inflate(bounds, -1 - padding.Left, -1 - padding.Top); // Content rect will be used to position text and images Rectangle contentBounds = Rectangle.Inflate(paddedBounds, -padding.Left, -padding.Top); @@ -117,7 +117,7 @@ public override Color GetBackgroundColor(PushButtonState state, bool isDefault) /// /// Draws the 3D effect border for the button. /// - public override void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault) + public override void DrawButtonBorder(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault) { // Save original smoothing mode to restore later SmoothingMode originalMode = graphics.SmoothingMode; diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs index 5bcf5abb01c..29c01385e13 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs @@ -30,7 +30,7 @@ internal class SystemButtonDarkModeRenderer : ButtonDarkModeRendererBase /// /// Draws button background with system styling (larger rounded corners). /// - public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor) + public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault, Color backColor) { // Shrink for DarkBorderGap and FocusBorderThickness Rectangle fillBounds = Rectangle.Inflate(bounds, -SystemStylePadding, -SystemStylePadding); @@ -108,7 +108,7 @@ public override Color GetBackgroundColor(PushButtonState state, bool isDefault) _ => DefaultColors.StandardBackColor }; - public override void DrawButtonBorder(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault) => + public override void DrawButtonBorder(Graphics graphics, Rectangle bounds, int borderSize, PushButtonState state, bool isDefault) => DrawButtonBorder(graphics, bounds, state, isDefault, false); ///