Skip to content

[NO MERGE] Add flicker-free UI mutation APIs#14735

Open
KlausLoeffelmann wants to merge 18 commits into
dotnet:feature/11.0from
KlausLoeffelmann:Net11Api_03_ImproveControlRendering
Open

[NO MERGE] Add flicker-free UI mutation APIs#14735
KlausLoeffelmann wants to merge 18 commits into
dotnet:feature/11.0from
KlausLoeffelmann:Net11Api_03_ImproveControlRendering

Conversation

@KlausLoeffelmann

@KlausLoeffelmann KlausLoeffelmann commented Jul 14, 2026

Copy link
Copy Markdown
Member

Closes #14585.
Contributes to #14694.

  • Adds composable painting and relocation suspension interfaces and disposable scopes.
  • Implements control-specific suspension hooks while preserving nested and handle-recreation semantics.
  • Adds ambient FormRevealMode and application-default support to reduce initial form flash.
  • Wires the feature through the Visual Basic Application Framework.
  • Includes focused tests for suspension balancing, async-safe scopes, form reveal behavior, and handle lifetimes.
Microsoft Reviewers: Open in CodeFlow

⚠️ NO MERGE (yet). This PR belongs to a stacked series of .NET 11 API PRs and does not compile on its own. The PRs must be merged in this order (each builds on the previous):

#14733#14734#14735#14737

(Net11Api_04 / #14736 is intentionally excluded from this sequence for now.) Please do not merge this PR until the preceding PRs in the sequence have been merged.

KlausLoeffelmann and others added 11 commits July 18, 2026 01:26
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 27022e2a-9eb8-4f58-9bfa-e79ca0d6c559
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements suspend painting and relocation scopes, deferred child positioning, and form appearance mode infrastructure for .NET 11.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rface

Splits DeferLocationChange out of this branch entirely (postponed pending an
anchor-layout-engine integration fix; tracked separately in
#12) and reworks ISupportSuspendPainting /
ISupportSuspendRelocation on Control to match the final API shape agreed in
dotnet#14585:

- Control implements ISupportSuspendPainting/ISupportSuspendRelocation via
  explicit interface implementation instead of public virtual methods, so the
  manual Begin/End pair does not become the primary IntelliSense surface on
  every Control-derived type. Protected virtual BeginSuspendPaintingCore() /
  EndSuspendPaintingCore() / BeginSuspendRelocationCore() /
  EndSuspendRelocationCore() are the new override points.
- ListView, ListBox, ComboBox, TreeView, RichTextBox override the ...Core()
  hooks instead of the old public virtual methods, still routing through
  their existing BeginUpdate/EndUpdate.
- SuspendPaintingScope and SuspendRelocationScope change from
  readonly ref struct to sealed class : IDisposable, so the scope can span an
  await in an asynchronous UI event handler (a ref struct cannot be hoisted
  into an async state machine - this was the flagship usage scenario in the
  original API proposal, and it did not compile against the ref struct
  version). Dispose is idempotent.
- Deletes DeferLocationChangeScope.cs and the Control.DeferLocationChange
  overloads entirely.
- Updates/removes tests accordingly; updates PublicAPI.Unshipped.txt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…inel

Matches the final API shape agreed in dotnet#14585:

- FormAppearanceMode -> FormRevealMode, adding Inherit = -1 as the ambient
  sentinel (Classic stays the CLR default value 0, matching the
  RightToLeft.Inherit / VisualStylesMode.Inherit precedent for ambient
  enums - the sentinel is a distinct value, not the zero value, so
  default(FormRevealMode) stays conservative).
- Form.FormRevealMode is now a real public virtual, PropertyStore-backed,
  [AmbientValue(Inherit)] property (previously there was no per-Form
  property at all - only the flat, process-wide
  Application.FormAppearanceMode existed). This lets a form such as a splash
  screen opt itself out of deferred reveal without touching the process-wide
  default. Resolution is flat (Form only, no Control-parent-chain): DWM
  cloaking only ever applies to top-level, non-MDI-child windows, so there is
  no hierarchy to walk, unlike VisualStylesMode's genuine control-nesting
  cascade.
- Application.FormAppearanceMode / SetFormAppearanceMode are replaced by
  three members: DefaultFormRevealMode (get; may return the unresolved
  Inherit sentinel, mirroring ColorMode returning the unresolved System
  value), SetDefaultFormRevealMode (freely reassignable, unlike the
  write-once SetDefaultVisualStylesMode - the effective default is derived in
  part from ColorMode/IsDarkModeEnabled, which are themselves mutable for the
  life of the process), and IsFormRevealDeferred (bool; the fully resolved
  answer: Deferred, or Inherit + IsDarkModeEnabled). This also fixes a
  compatibility problem in the original design: the old default was
  unconditionally Deferred whenever SetFormAppearanceMode was never called,
  an opt-out behavior change for every existing app; tying the Inherit
  resolution to dark mode means an app that never touches SystemColorMode
  sees no behavior change, while the scenario the feature exists for
  (dark-mode startup flash) is fixed by default.
- ShouldUseDeferredAppearanceCloak now reads the resolved Form.FormRevealMode
  instead of the old flat Application-only check, so a per-Form override is
  actually honored.
- Adds SR.resx/xlf entries for the new property's designer description.
- Updates/adds tests; updates PublicAPI.Unshipped.txt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds Microsoft.VisualBasic.ApplicationServices.ApplyApplicationDefaultsEventArgs.FormRevealMode,
following the exact same pattern already established for ColorMode and
HighDpiMode in that class, per dotnet#14585's API Proposal.

WindowsFormsApplicationBase now carries a _formRevealMode shadow field
(defaulting to FormRevealMode.Classic, matching the existing conservative
default for _colorMode) and a protected FormRevealMode property, feeds it
into the ApplyApplicationDefaultsEventArgs constructor alongside
MinimumSplashScreenDisplayTime/HighDpiMode/ColorMode, reads back whatever the
ApplyApplicationDefaults event handler set, and calls
Application.SetDefaultFormRevealMode(_formRevealMode) at the end of
OnInitialize alongside the existing Application.SetColorMode(_colorMode)
call.

This completes work anticipated but never finished in an earlier .NET 9
Visual Styles attempt at this same VB Application Framework extension point
(OnInitialize already carried a comment claiming "We feed the defaults for
HighDpiMode, ColorMode, VisualStylesMode to the EventArgs", but only
HighDpiMode/ColorMode were ever actually wired up).

Updates PublicAPI.Unshipped.txt for Microsoft.VisualBasic.Forms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 20883488-cf1e-4ad3-a681-0724e9f16777
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 20883488-cf1e-4ad3-a681-0724e9f16777
Remove the risky relocation API and add controlled layout traversal to painting scopes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c3dc32b-7202-48c4-b0c7-f40a0a681ee2
KlausLoeffelmann and others added 5 commits July 21, 2026 00:35
ShouldUseDeferredAppearanceCloak() gated the DWM cloak on Visible == true, but it is only evaluated from OnHandleCreated, where a top-level form's window is still hidden (WS_VISIBLE is cleared from the create params and the show is deferred). The Visible state bit is not set until WM_SHOWWINDOW is processed, so the guard was always false, the cloak never engaged, and the window was shown uncloaked - still producing the default-background flash the mode is meant to prevent.

Remove the Visible term so the already-hidden window is cloaked at handle creation. This covers Show, ShowDialog (which creates the handle via CreateControl before showing) and handle recreation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 78934db)
Flicker-free UI mutation API:
- Rename LayoutSuspendTraversal members for clarity and redefine semantics:
  None -> TargetOnly (0), TopLevelOnly -> TargetAndChildren (1, new: target plus
  immediate children), Traverse -> TargetAndDescendants (2). The painting-only
  (no layout suspension) case is now served solely by the parameterless
  SuspendPainting overload.
- Make SuspendPaintingScope internal; the three ControlMutationExtensions
  .SuspendPainting overloads now return IDisposable. The await-safe / idempotent
  guidance moves to the public extension-method docs so it stays discoverable.
- Document that Control implements ISupportSuspendPainting explicitly (callers use
  the IDisposable scope; overriders use Begin/EndSuspendPaintingCore).

FormRevealMode:
- Add the FormRevealModeChanged event and protected virtual OnFormRevealModeChanged,
  raised from the setter when the effective value changes.

Updates PublicAPI.Unshipped.txt, SR.resx, and ControlTests.Methods.cs (including a
new TargetAndChildren test).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 64d20bbb-13df-4e59-8bcc-c307909b03f8
Generated by the build after adding the FormRevealModeChanged event description
to SR.resx. Keeps the localized .xlf files in sync with the neutral resources.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 64d20bbb-13df-4e59-8bcc-c307909b03f8
Per follow-up review: the parameterless SuspendPainting overload already covers
the painting-only case, but keeping an explicit None = 0 restores the intuitive
default (no layout suspension) and avoids default(enum) silently suspending.
TargetAndChildren is removed because the distinction from TargetOnly was subtle
and confusing (a container's SuspendLayout already holds its children's layout).

Final enum: None = 0, TargetOnly = 1, TargetAndDescendants = 2.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 64d20bbb-13df-4e59-8bcc-c307909b03f8
…rst paint

Deferred reveal cloaked the form at OnHandleCreated and uncloaked on the form's
own first WM_PAINT. That paint fires before the child controls - separate child
windows - paint their first frame, so the window was revealed mid-paint and the
tail of the startup flash (controls popping in) was still visible.

Reveal only after the whole control tree has painted its first frame:
- Add Form.RevealDeferredAppearance(): force a synchronous full-tree paint of the
  still-cloaked window (RedrawWindow RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN|
  RDW_UPDATENOW), then uncloak, so the finished frame appears at once. It is a
  no-op once revealed / never cloaked, and guarded against re-entrancy since
  RDW_UPDATENOW dispatches WM_PAINT synchronously.
- Trigger it from OnShown (CallShownEvent) as the primary, guaranteed one-shot
  point that runs before the first natural WM_PAINT, and keep the WM_PAINT hook as
  an idempotent fallback so the window can never be revealed mid-paint or stay
  stuck cloaked.

Adds a safety-invariant test: a shown Deferred form must not remain cloaked.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 64d20bbb-13df-4e59-8bcc-c307909b03f8
@KlausLoeffelmann
KlausLoeffelmann changed the base branch from main to feature/11.0 July 22, 2026 23:01
@KlausLoeffelmann KlausLoeffelmann changed the title Add flicker-free UI mutation APIs [NO MERGE] Add flicker-free UI mutation APIs Jul 22, 2026
KlausLoeffelmann and others added 2 commits July 23, 2026 04:24
Several XML doc comments referenced <see cref="Forms.FormRevealMode"/>, an
inconsistent partial-namespace qualification for the FormRevealMode enum. Use the
unqualified enum name FormRevealMode (as elsewhere in the file, e.g.
FormRevealMode.Inherit) so the cref resolves cleanly and matches the surrounding
"a FormRevealMode value" wording.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 19883d86-6d5f-4d51-94c2-81d0bf3fc3ff
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 86cc3454-1c71-4d7f-a766-231b55099101
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[.NET 11 API Proposal] Add APIs for performance improved, flicker-free WinForms UI mutation

1 participant