From bd5bec6fc37159fd2cfd4e7a321889076fdc9c6c Mon Sep 17 00:00:00 2001 From: "Ricardo Bossan (BEYONDSOFT CONSULTING INC) (from Dev Box)" Date: Wed, 5 Aug 2026 22:09:42 -0300 Subject: [PATCH 1/4] Fixes PrintPreviewControl displays a black preview page after assigning an empty PrintDocument Fixes #14838 ## Proposed changes - In `PrintPreviewControl.DrawPages`, fill each page rectangle with a fixed `Color.White` "paper" background instead of the control's `ForeColor`. - Added a regression unit test asserting a page with no drawable content renders white, not `ForeColor`. ## Customer Impact - Print preview pages render as white paper again, regardless of the control's `ForeColor`. - Fixes the solid-black preview page reported for a `PrintDocument` that produces no printable content, and removes the general risk of the page background matching a dark `ForeColor`. ## Regression? - Yes. Regression from #14424 (merged 2026-07-02), which correctly changed `ResetForeColor()`'s default from `Color.White` to `SystemColors.ControlText` to fix #14420. `DrawPages()` separately reused `ForeColor` to fill each page's background (present since the original .NET Framework port) and only rendered white by coincidence, because the old default happened to match. This PR doesn't touch #14424's changes (`ForeColor`, `ResetForeColor`, `ShouldSerializeForeColor`, `DrawMessage`), so #14420 stays fixed; it only changes the unrelated page-background fill in `DrawPages`. ## Risk - Low. Single fill-color change, scoped to the page-background rectangle in `DrawPages`. Verified it doesn't touch `ForeColor`, `ResetForeColor`, `ShouldSerializeForeColor`, `_isForeColorSet`, or `DrawMessage` (the members #14424 added/changed for #14420). - The white fill isn't theme-aware, matching how this code behaved from the original .NET Framework port through #14424 (`Color.White`). No issue, test, or doc comment in this control's history ties `ForeColor` to page-background color (#13861/#13863/#14420/#14424 are all specifically about the "no printable content" message text), so this restores the long-standing default rather than removing a documented capability. If someone was relying on `ForeColor` to tint the blank-page fill as a side effect, that (undocumented) behavior is gone; open to making it configurable separately if that's a real use case. ## Screenshots ### Before ### After ## Test methodology - Added `PrintPreviewControl_PageWithNoImage_RendersWhiteNotForeColor`: builds a `PrintPreviewControl` with `ForeColor = Color.Black` and injects a synthetic page with no image (`new PreviewPageInfo(image: null, physicalSize: ...)`, standing in for an empty `PrintDocument`'s output), renders to a bitmap, and asserts the page interior is white rather than black. - Confirmed the new test fails against the pre-fix code (reproducing the reported black page) and passes after the fix, to make sure it actually exercises the regression. - Ran the full `PrintPreviewControlTests` class locally: 8/8 passing. - Manually reproduced the issue end-to-end in `ScratchProject`: reverted the fix, rebuilt, confirmed the solid black page shown above, then restored the fix, rebuilt, and confirmed the page renders white (screenshots above). ## Test environment(s) - 11.0.100-preview.6.26359.118 --- .../Forms/Printing/PrintPreviewControl.cs | 4 ++- .../Printing/PrintPreviewControlTests.cs | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs b/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs index 342ff10af83..d7eda22bfb9 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs @@ -690,7 +690,9 @@ private void DrawPages(Graphics g, Rectangle rect, PreviewPageInfo[] pages, Brus { Rectangle box = pageRenderArea[i]; g.DrawRectangle(Pens.Black, box); - using (var brush = ForeColor.GetCachedSolidBrushScope()) + + // Page background is fixed white; ForeColor is unrelated (it colors message text only). + using (var brush = Color.White.GetCachedSolidBrushScope()) { g.FillRectangle(brush, box); } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs index eaaf19a51fc..4bac3330e21 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs @@ -4,6 +4,7 @@ #nullable disable using System.Drawing; +using System.Drawing.Printing; namespace System.Windows.Forms.Tests; @@ -68,6 +69,32 @@ public void PrintPreviewControl_ForeColorReset_ShouldSerializeReturnsFalse() Assert.Equal(SystemColors.ControlText.ToArgb(), control.ForeColor.ToArgb()); } + [WinFormsFact] + public void PrintPreviewControl_PageWithNoImage_RendersWhiteNotForeColor() + { + // Regression test for #14838: a page with no drawable content (e.g. from an empty + // PrintDocument) must render as white paper, not as a solid ForeColor rectangle. + using PrintPreviewControl control = new() + { + ForeColor = Color.Black, + Size = new Size(200, 200) + }; + + control.CreateControl(); + + PreviewPageInfo[] pageInfo = [new(image: null, physicalSize: new Size(850, 1100))]; + control.TestAccessor.Dynamic._pageInfo = pageInfo; + + using Bitmap bitmap = new(control.Width, control.Height); + control.DrawToBitmap(bitmap, new Rectangle(Point.Empty, control.Size)); + + // The single page fills nearly the whole control, so the center pixel lands well + // inside the page interior, away from its 1px black border. + Color centerPixel = bitmap.GetPixel(bitmap.Width / 2, bitmap.Height / 2); + + Assert.Equal(Color.White.ToArgb(), centerPixel.ToArgb()); + } + [Fact] public void ShowPrintPreviewControlHighContrast_BackColorIsCorrect() { From 66f365a97ca98c51f5f1230b0f5582406f789357 Mon Sep 17 00:00:00 2001 From: "Ricardo Bossan (BEYONDSOFT CONSULTING INC) (from Dev Box)" Date: Thu, 6 Aug 2026 22:27:58 -0300 Subject: [PATCH 2/4] Handles feedback --- .../Forms/Printing/PrintPreviewControl.cs | 6 ++-- .../Printing/PrintPreviewControlTests.cs | 31 +++++++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs b/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs index d7eda22bfb9..9e596257693 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs @@ -691,8 +691,10 @@ private void DrawPages(Graphics g, Rectangle rect, PreviewPageInfo[] pages, Brus Rectangle box = pageRenderArea[i]; g.DrawRectangle(Pens.Black, box); - // Page background is fixed white; ForeColor is unrelated (it colors message text only). - using (var brush = Color.White.GetCachedSolidBrushScope()) + // Default page fill is white (paper); an explicitly set ForeColor is still honored, + // as it always has been. + Color pageColor = _isForeColorSet ? ForeColor : Color.White; + using (var brush = pageColor.GetCachedSolidBrushScope()) { g.FillRectangle(brush, box); } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs index 4bac3330e21..6788b199e0e 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs @@ -70,13 +70,13 @@ public void PrintPreviewControl_ForeColorReset_ShouldSerializeReturnsFalse() } [WinFormsFact] - public void PrintPreviewControl_PageWithNoImage_RendersWhiteNotForeColor() + public void PrintPreviewControl_PageWithNoImage_DefaultForeColor_RendersWhite() { // Regression test for #14838: a page with no drawable content (e.g. from an empty - // PrintDocument) must render as white paper, not as a solid ForeColor rectangle. + // PrintDocument), with ForeColor left at its default, must render as white paper, + // not as a solid black rectangle. using PrintPreviewControl control = new() { - ForeColor = Color.Black, Size = new Size(200, 200) }; @@ -95,6 +95,31 @@ public void PrintPreviewControl_PageWithNoImage_RendersWhiteNotForeColor() Assert.Equal(Color.White.ToArgb(), centerPixel.ToArgb()); } + [WinFormsFact] + public void PrintPreviewControl_PageWithNoImage_ExplicitForeColor_RendersForeColor() + { + // ForeColor has driven the page background fill since the original .NET Framework port; + // an explicitly set value must still be honored (see PR #14857 discussion), not overridden + // by the white default that only applies when ForeColor was never set. + using PrintPreviewControl control = new() + { + ForeColor = Color.Red, + Size = new Size(200, 200) + }; + + control.CreateControl(); + + PreviewPageInfo[] pageInfo = [new(image: null, physicalSize: new Size(850, 1100))]; + control.TestAccessor.Dynamic._pageInfo = pageInfo; + + using Bitmap bitmap = new(control.Width, control.Height); + control.DrawToBitmap(bitmap, new Rectangle(Point.Empty, control.Size)); + + Color centerPixel = bitmap.GetPixel(bitmap.Width / 2, bitmap.Height / 2); + + Assert.Equal(Color.Red.ToArgb(), centerPixel.ToArgb()); + } + [Fact] public void ShowPrintPreviewControlHighContrast_BackColorIsCorrect() { From 6c989d654b93298f3d24d7813f8f6226268963da Mon Sep 17 00:00:00 2001 From: "Ricardo Bossan (BEYONDSOFT CONSULTING INC) (from Dev Box)" Date: Thu, 13 Aug 2026 22:29:23 -0300 Subject: [PATCH 3/4] Handles feedback --- .../Forms/Printing/PrintPreviewControl.cs | 20 +++++++++-- .../Printing/PrintPreviewControlTests.cs | 36 +++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs b/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs index 9e596257693..03425e04e63 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControl.cs @@ -58,7 +58,6 @@ public PrintPreviewControl() { ResetBackColor(); ResetForeColor(); - ForeColorChanged += (_, _) => _isForeColorSet = true; Size = new Size(100, 100); SetStyle(ControlStyles.ResizeRedraw, false); SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true); @@ -296,6 +295,22 @@ public override string Text internal override bool ShouldSerializeBackColor() => !BackColor.Equals(SystemColors.AppWorkspace); + /// + /// Gets or sets the foreground color of the "no printable content" message text, and (when + /// explicitly set) each previewed page's background. + /// + public override Color ForeColor + { + get => base.ForeColor; + set + { + // Tracked here, not via ForeColorChanged: that event only fires on an actual value + // change, so reassigning the current value would otherwise look like "never set". + _isForeColorSet = true; + base.ForeColor = value; + } + } + [EditorBrowsable(EditorBrowsableState.Never)] public override void ResetForeColor() { @@ -691,8 +706,7 @@ private void DrawPages(Graphics g, Rectangle rect, PreviewPageInfo[] pages, Brus Rectangle box = pageRenderArea[i]; g.DrawRectangle(Pens.Black, box); - // Default page fill is white (paper); an explicitly set ForeColor is still honored, - // as it always has been. + // Default page fill is white; an explicitly set ForeColor is still honored, as it always has been. Color pageColor = _isForeColorSet ? ForeColor : Color.White; using (var brush = pageColor.GetCachedSolidBrushScope()) { diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs index 6788b199e0e..294580d397b 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/Printing/PrintPreviewControlTests.cs @@ -45,7 +45,7 @@ public void PrintPreviewControl_ForeColorNotSet_ShouldSerializeReturnsFalse() [Fact] public void PrintPreviewControl_ForeColorWhiteExplicitlySet_ShouldSerializeReturnsTrue() { - // Regression: #14420 - explicitly setting White must be distinguishable from the default. + // Regression: #14420 (explicitly setting White must be distinguishable from the default). PrintPreviewControl control = new() { ForeColor = Color.White }; Assert.True(control.TestAccessor.Dynamic.ShouldSerializeForeColor()); @@ -73,8 +73,8 @@ public void PrintPreviewControl_ForeColorReset_ShouldSerializeReturnsFalse() public void PrintPreviewControl_PageWithNoImage_DefaultForeColor_RendersWhite() { // Regression test for #14838: a page with no drawable content (e.g. from an empty - // PrintDocument), with ForeColor left at its default, must render as white paper, - // not as a solid black rectangle. + // PrintDocument), with ForeColor left at its default, must render as white paper, not as + // a solid black rectangle. using PrintPreviewControl control = new() { Size = new Size(200, 200) @@ -98,9 +98,8 @@ public void PrintPreviewControl_PageWithNoImage_DefaultForeColor_RendersWhite() [WinFormsFact] public void PrintPreviewControl_PageWithNoImage_ExplicitForeColor_RendersForeColor() { - // ForeColor has driven the page background fill since the original .NET Framework port; - // an explicitly set value must still be honored (see PR #14857 discussion), not overridden - // by the white default that only applies when ForeColor was never set. + // ForeColor has driven the page background since the .NET Framework days; an explicit + // value must still be honored, not overridden by the white-when-unset default. using PrintPreviewControl control = new() { ForeColor = Color.Red, @@ -120,6 +119,31 @@ public void PrintPreviewControl_PageWithNoImage_ExplicitForeColor_RendersForeCol Assert.Equal(Color.Red.ToArgb(), centerPixel.ToArgb()); } + [WinFormsFact] + public void PrintPreviewControl_PageWithNoImage_ExplicitForeColorMatchingDefault_RendersForeColor() + { + // Assigning ForeColor to the value it already holds must still count as explicit, even + // though ForeColorChanged doesn't fire for a same-value reassignment. + using PrintPreviewControl control = new() + { + Size = new Size(200, 200) + }; + control.ForeColor = SystemColors.ControlText; + + control.CreateControl(); + + PreviewPageInfo[] pageInfo = [new(image: null, physicalSize: new Size(850, 1100))]; + control.TestAccessor.Dynamic._pageInfo = pageInfo; + + using Bitmap bitmap = new(control.Width, control.Height); + control.DrawToBitmap(bitmap, new Rectangle(Point.Empty, control.Size)); + + Color centerPixel = bitmap.GetPixel(bitmap.Width / 2, bitmap.Height / 2); + + Assert.Equal(SystemColors.ControlText.ToArgb(), centerPixel.ToArgb()); + Assert.True(control.TestAccessor.Dynamic.ShouldSerializeForeColor()); + } + [Fact] public void ShowPrintPreviewControlHighContrast_BackColorIsCorrect() { From 0b31a3688efe7630a817c9b9fbdad1bd9e8b07fe Mon Sep 17 00:00:00 2001 From: "Ricardo Bossan (BEYONDSOFT CONSULTING INC) (from Dev Box)" Date: Thu, 13 Aug 2026 23:02:30 -0300 Subject: [PATCH 4/4] Adds to public API --- src/System.Windows.Forms/PublicAPI.Unshipped.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index fd3fe1e230a..1b299c8b911 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -109,3 +109,5 @@ virtual System.Windows.Forms.Control.OnParentVisualStylesModeChanged(System.Even virtual System.Windows.Forms.Control.OnVisualStylesModeChanged(System.EventArgs! e) -> void virtual System.Windows.Forms.Control.VisualStylesMode.get -> System.Windows.Forms.VisualStylesMode virtual System.Windows.Forms.Control.VisualStylesMode.set -> void +override System.Windows.Forms.PrintPreviewControl.ForeColor.get -> System.Drawing.Color +override System.Windows.Forms.PrintPreviewControl.ForeColor.set -> void