Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/System.Windows.Forms/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1606,7 +1606,6 @@ override System.Windows.Forms.ProgressBar.DoubleBuffered.set -> void
override System.Windows.Forms.ProgressBar.Font.get -> System.Drawing.Font!
override System.Windows.Forms.ProgressBar.Font.set -> void
override System.Windows.Forms.ProgressBar.OnBackColorChanged(System.EventArgs! e) -> void
override System.Windows.Forms.ProgressBar.OnCreateControl() -> void
override System.Windows.Forms.ProgressBar.OnForeColorChanged(System.EventArgs! e) -> void
override System.Windows.Forms.ProgressBar.OnHandleCreated(System.EventArgs! e) -> void
override System.Windows.Forms.ProgressBar.OnHandleDestroyed(System.EventArgs! e) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public partial class ProgressBar : Control
private int _marqueeAnimationSpeed = 100;

private static readonly Color s_defaultForeColor = SystemColors.Highlight;
private static readonly Color s_defaultDarkModeBackColor = SystemColors.ControlText;

private ProgressBarStyle _style = ProgressBarStyle.Blocks;

Expand Down Expand Up @@ -71,31 +72,6 @@ protected override CreateParams CreateParams
}
}

protected override void OnCreateControl()
{
base.OnCreateControl();

// If SystemColorMode is enabled, we need to disable the Visual Styles
// so Windows allows setting Fore- and Background color.
// There are more ideal ways imaginable, but this does the trick for now.

if (Application.IsDarkModeEnabled)
{
if (!ShouldSerializeBackColor())
{
BackColor = SystemColors.ControlDarkDark;
}

if (!ShouldSerializeForeColor())
{
ForeColor = SystemColors.Highlight;
}

// Disables Visual Styles for the ProgressBar.
PInvoke.SetWindowTheme(HWND, " ", " ");
}
}

[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool AllowDrop
Expand Down Expand Up @@ -133,6 +109,8 @@ public ProgressBarStyle Style
if (IsHandleCreated)
{
RecreateHandle();
// Re-apply theming after handle recreation
ApplyTheming();
Comment thread
LeafShi1 marked this conversation as resolved.
}

if (_style == ProgressBarStyle.Marquee)
Expand Down Expand Up @@ -349,7 +327,7 @@ protected override void OnBackColorChanged(EventArgs e)
base.OnBackColorChanged(e);
if (IsHandleCreated)
{
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBKCOLOR, 0, BackColor.ToWin32());
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBKCOLOR, 0, GetEffectiveBackColor().ToWin32());
}
}

Expand All @@ -358,7 +336,7 @@ protected override void OnForeColorChanged(EventArgs e)
base.OnForeColorChanged(e);
if (IsHandleCreated)
{
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBARCOLOR, 0, ForeColor.ToWin32());
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBARCOLOR, 0, GetEffectiveForeColor().ToWin32());
}
}

Expand Down Expand Up @@ -603,13 +581,17 @@ public void Increment(int value)
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);

ApplyTheming();

if (IsHandleCreated)
{
PInvokeCore.SendMessage(this, PInvoke.PBM_SETRANGE32, (WPARAM)_minimum, (LPARAM)_maximum);
PInvokeCore.SendMessage(this, PInvoke.PBM_SETSTEP, (WPARAM)_step);
PInvokeCore.SendMessage(this, PInvoke.PBM_SETPOS, (WPARAM)_value);
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBKCOLOR, (WPARAM)0, (LPARAM)BackColor);
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBARCOLOR, (WPARAM)0, (LPARAM)ForeColor);

PInvokeCore.SendMessage(this, PInvoke.PBM_SETBKCOLOR, 0, GetEffectiveBackColor().ToWin32());
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBARCOLOR, 0, GetEffectiveForeColor().ToWin32());
}

StartMarquee();
Expand Down Expand Up @@ -701,10 +683,56 @@ private void UpdatePos()
/// </remarks>
private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e)
{
if (IsHandleCreated)
if (!IsHandleCreated)
{
return;
}

// Only react to changes that can affect colors or theme changes.
if (e.Category is not UserPreferenceCategory.Color and not UserPreferenceCategory.General)
{
return;
}

ApplyTheming();

PInvokeCore.SendMessage(this, PInvoke.PBM_SETBARCOLOR, 0, GetEffectiveForeColor().ToWin32());
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBKCOLOR, 0, GetEffectiveBackColor().ToWin32());
}

private Color GetEffectiveBackColor()
{
if (ShouldSerializeBackColor())
{
return BackColor;
}

return Application.IsDarkModeEnabled ? s_defaultDarkModeBackColor : BackColor;
}

private Color GetEffectiveForeColor()
{
return ShouldSerializeForeColor() ? ForeColor : s_defaultForeColor;
}

private void ApplyTheming()
{
if (!IsHandleCreated)
{
return;
}

if (Application.IsDarkModeEnabled)
{
// In dark mode on newer Windows builds, style switching can produce mixed rendering
// across Blocks/Continuous/Marquee. Disable visual styles and drive colors via PBM_SET*COLOR
// for consistent appearance.
PInvoke.SetWindowTheme(HWND, " ", " ");
}
Comment thread
LeafShi1 marked this conversation as resolved.
else
{
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBARCOLOR, 0, ForeColor.ToWin32());
PInvokeCore.SendMessage(this, PInvoke.PBM_SETBKCOLOR, 0, BackColor.ToWin32());
// Restore default theming when dark mode and custom colors are no longer active.
PInvoke.SetWindowTheme(HWND, (PCWSTR)null, (PCWSTR)null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2165,6 +2165,69 @@ public void ProgressBar_OnHandleCreated_InvokeWithHandle_CallsHandleCreated(Prog
Assert.True(control.IsHandleCreated);
}

[WinFormsFact]
public void ProgressBar_RecreateHandle_WithAmbientBackColorInLightMode_SendsAmbientBackColor()
{
using Form parent = new()
{
BackColor = Color.OrangeRed
};
using SubProgressBar progressBar = new();
parent.Controls.Add(progressBar);

parent.CreateControl();
Assert.NotEqual(IntPtr.Zero, progressBar.Handle);
Assert.Equal(parent.BackColor, progressBar.BackColor);

progressBar.ResetBkColorTracking();
progressBar.RecreateHandle();

Assert.True(progressBar.SetBkColorMessageCount > 0);
Assert.Equal(ColorTranslator.ToWin32(progressBar.BackColor), progressBar.LastSetBkColorMessage);
}

[WinFormsFact]
public void ProgressBar_StyleSwitch_WithCustomBackColorInLightMode_SendsCustomBackColor()
{
using Form parent = new();
using SubProgressBar progressBar = new()
{
BackColor = Color.White,
Style = ProgressBarStyle.Blocks
};
parent.Controls.Add(progressBar);

parent.CreateControl();
Assert.NotEqual(IntPtr.Zero, progressBar.Handle);

progressBar.ResetColorTracking();
progressBar.Style = ProgressBarStyle.Continuous;

Assert.True(progressBar.SetBkColorMessageCount > 0);
Assert.Equal(ColorTranslator.ToWin32(progressBar.BackColor), progressBar.LastSetBkColorMessage);
}

[WinFormsFact]
public void ProgressBar_StyleSwitch_WithCustomForeColorInLightMode_SendsCustomForeColor()
{
using Form parent = new();
using SubProgressBar progressBar = new()
{
ForeColor = Color.LimeGreen,
Style = ProgressBarStyle.Blocks
};
parent.Controls.Add(progressBar);

parent.CreateControl();
Assert.NotEqual(IntPtr.Zero, progressBar.Handle);

progressBar.ResetColorTracking();
progressBar.Style = ProgressBarStyle.Continuous;

Assert.True(progressBar.SetBarColorMessageCount > 0);
Assert.Equal(ColorTranslator.ToWin32(progressBar.ForeColor), progressBar.LastSetBarColorMessage);
}

[WinFormsTheory]
[NewAndDefaultData<EventArgs>]
public void ProgressBar_OnHandleDestroyed_Invoke_CallsHandleDestroyed(EventArgs eventArgs)
Expand Down Expand Up @@ -2641,6 +2704,8 @@ public class SubProgressBar : ProgressBar

public new void CreateHandle() => base.CreateHandle();

public new void RecreateHandle() => base.RecreateHandle();

public new AutoSizeMode GetAutoSizeMode() => base.GetAutoSizeMode();

public new bool GetStyle(ControlStyles flag) => base.GetStyle(flag);
Expand Down Expand Up @@ -2674,5 +2739,42 @@ public class SubProgressBar : ProgressBar
public new void OnRightToLeftLayoutChanged(EventArgs e) => base.OnRightToLeftLayoutChanged(e);

public new void SetStyle(ControlStyles flag, bool value) => base.SetStyle(flag, value);

public int? LastSetBkColorMessage { get; private set; }

public int? LastSetBarColorMessage { get; private set; }

public int SetBkColorMessageCount { get; private set; }

public int SetBarColorMessageCount { get; private set; }

public void ResetColorTracking()
{
LastSetBkColorMessage = null;
LastSetBarColorMessage = null;
SetBkColorMessageCount = 0;
SetBarColorMessageCount = 0;
}

public void ResetBkColorTracking()
{
ResetColorTracking();
}

protected override void WndProc(ref Message m)
{
if (m.Msg == (int)PInvoke.PBM_SETBKCOLOR)
{
SetBkColorMessageCount++;
LastSetBkColorMessage = unchecked((int)m.LParam);
}
else if (m.Msg == (int)PInvoke.PBM_SETBARCOLOR)
{
SetBarColorMessageCount++;
LastSetBarColorMessage = unchecked((int)m.LParam);
}

base.WndProc(ref m);
}
}
}