Defer document and tool content materialization to the next dispatcher frame#1086
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f83a4a0bbc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/Dock.Controls.DeferredContentControl/DeferredContentControl.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a719c5c71b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/Dock.Controls.DeferredContentControl/DeferredContentControl.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 99ed42cbff
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| internal void ApplyDeferredState(object? content, IDataTemplate? contentTemplate, TimeSpan revealDuration) | ||
| { | ||
| var hadPresentedChild = Child is not null; | ||
| _suppressDeferredUpdates = true; | ||
|
|
There was a problem hiding this comment.
Preserve applied presenter state in templated-parent path
When DeferredContentPresenter is used as PART_ContentPresenter inside a DeferredContentControl template (especially if the template binds Content/ContentTemplate), this method applies values to the base presenter but never updates _appliedContent, _appliedContentTemplate, or _appliedVersion. Because QueueDeferredPresentation() short-circuits for TemplatedParent is DeferredContentControl, ApplyDeferredPresentation() is never called to refresh those fields, so the next OnPropertyChanged/RestoreAppliedState() can roll the presenter back to stale values (often null), causing visible blanking/flicker before the deferred batch runs.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec927e874f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| control.Opacity = RevealStartingOpacity; | ||
| Dispatcher.UIThread.Post(() => control.SetCurrentValue(Visual.OpacityProperty, 1D), DispatcherPriority.Background); |
There was a problem hiding this comment.
Preserve bound opacity during deferred reveal staging
The reveal path writes control.Opacity directly before posting the final value, which introduces a local value on Opacity and can override an existing style/binding-driven opacity on the host. In templates that intentionally set non-default opacity (for dimming, transitions, or state-based styling), a deferred content swap will force the presenter toward 1.0, causing visible regressions and potentially breaking the original binding source for later updates.
Useful? React with 👍 / 👎.
PR Summary: Deferred Dock Content Materialization
Branch
feature/deferred-content-materializationPull Request
masterfeature/deferred-content-materializationCommit Series
c54c8c01cAdd deferred dock content hostba153af47Add deferred content headless testsf83a4a0bbIgnore local report artifacts8e80a6707Extract deferred content hosts into standalone package6ecdbc499Document deferred content control package8944d52d5Support standard content presenter templates1ec4ac487Batch deferred content realization per framef580fabf3Prevent deferred queue starvation1fd6dc8bbAdd configurable deferred presentation budgetsa719c5c71Document deferred presentation budget settings16b5eca4aBound deferred flushes to one queue scanf9cbbd27bDetach removed windows from owner graph9f4ef42a3Add scoped deferred presentation timelines8473c5985Add deferred content timeline sample6d1a0d902Document deferred presentation timelines9e0680eb3Refine deferred content reveal behavior99ed42cbfDocument deferred content reveal behaviorProblem
Large Dock layouts were still paying too much work during initial measure because heavy content hosts were eagerly triggering presenter realization, logical-tree attachment, and style/theme activation.
The first pass of this branch introduced deferred hosts and bounded batching, but two practical gaps remained:
In addition, the sample work exposed a presenter-contract edge case: time-budgeted standalone presenter paths needed more reliable delayed scheduling, and the sample needed to use the same template-binding pattern as real Dock presenter hosts.
After that, reveal smoothing exposed two more runtime issues:
Final Scope
This branch now covers four layers:
1. Built-in theme deferral
The built-in Fluent and Simple themes defer the main expensive content hosts:
DocumentContentControlToolContentControlDocumentControlToolControlMdiDocumentWindowSplitViewDockControlPinnedDockControlDockControlRootDockControlToolChromeControlHostWindowPaths that intentionally stay eager:
DocumentControlManaged floating-window content still has an explicit synchronous opt-out because deferring hosted live dock controls caused regressions.
2. Deferred timeline scoping
The standalone
Dock.Controls.DeferredContentControlpackage now supports scoped deferred scheduling instead of only one global queue.New API surface:
DeferredContentPresentationTimelineDeferredContentScheduling.TimelineDeferredContentScheduling.DelayDeferredContentScheduling.OrderDeferredContentPresentationSettings.DefaultTimelineDeferredContentPresentationSettings.InitialDelayDeferredContentPresentationSettings.FollowUpDelayThis allows:
3. Sample and documentation
The branch adds a focused sample app and full documentation for:
DeferredContentControl,DeferredContentPresenter.4. Deferred reveal smoothing
The deferred package now includes a bounded reveal transition for deferred replacements:
RevealDurationis configurable on both the shared default timeline and scoped timelines,That keeps the UI refresh smoother without blanking startup content or making tool/document tab switches look unloaded.
Approach
The deferred package now has two host types:
DeferredContentControlfor templates that can use aContentControlDeferredContentPresenterfor templates that must preserve aContentPresentercontractBoth hosts keep requested
ContentandContentTemplate, enqueue themselves onto a deferred timeline, and apply the latest coalesced state only when the timeline grants them a turn.The queue implementation now supports:
On top of scheduling, deferred hosts now support a short reveal transition that is applied only when replacing already-presented content.
Key Implementation Details
New package and public API
Files:
src/Dock.Controls.DeferredContentControl/Dock.Controls.DeferredContentControl.csprojsrc/Dock.Controls.DeferredContentControl/DeferredContentControl.cssrc/Dock.Controls.DeferredContentControl/Properties/AssemblyInfo.csKey points:
AssemblyInfo.cspreserves the Avalonia XML namespace mapping so theme XAML did not need a contract change.IDeferredContentPresentationremains the public opt-out contract for content that must stay synchronous.Queue behavior
File:
src/Dock.Controls.DeferredContentControl/DeferredContentControl.csFinal queue behavior:
BudgetModesupportsItemCountandRealizationTime.MaxPresentationsPerPassandMaxRealizationTimePerPassbound per-pass work.InitialDelaycontrols when a newly queued target first becomes due.FollowUpDelaycontrols later passes while pending work remains.Ordervalues run first, and FIFO order is preserved for ties.Task.Delayplus UI-thread reposting, which made delayed presenter/time-budget paths reliable in both tests and the desktop sample.The queue hot path was also tightened to avoid the LINQ/array allocation regression introduced during the first scoped-timeline implementation.
Presenter-contract path
File:
src/Dock.Controls.DeferredContentControl/DeferredContentControl.csDeferredContentPresenternow:This was necessary so presenter-contract templates actually defer the same way control-host templates do.
Reveal behavior
File:
src/Dock.Controls.DeferredContentControl/DeferredContentControl.csFinal reveal behavior:
RevealDurationlives onDeferredContentPresentationTimelineandDeferredContentPresentationSettings.0.Sample app
Files:
samples/DockDeferredContentSample/DockDeferredContentSample.csprojsamples/DockDeferredContentSample/MainWindow.axamlsamples/DockDeferredContentSample/ViewModels/MainWindowViewModel.cssamples/DockDeferredContentSample/Controls/PresenterCardHost.cssamples/DockDeferredContentSample/README.mdKey points:
PresenterCardHost) that feeds an innerDeferredContentPresenterthrough template bindings.Tests
Files:
tests/Dock.Avalonia.HeadlessTests/DeferredContentControlTests.cstests/Dock.Avalonia.HeadlessTests/HostWindowThemeChangeTests.cstests/Dock.Avalonia.HeadlessTests/FactoryWindowManagementTests.csCoverage now includes:
ContentPresenterfallback templates,ItemsControl,Documentation
Files:
docfx/articles/dock-deferred-content.mddocfx/articles/dock-deferred-content-sample.mddocfx/articles/toc.ymldocfx/index.mdREADME.mdThe documentation now explains:
DeferredContentControlversusDeferredContentPresenter,DelayandOrderinteract with both budget modes,RevealDurationworks,Review and Follow-up Fixes Included
This branch also includes the fixes discovered during review and validation:
Content/ContentTemplatestate.ContentPresentertemplate compatibility.Validation
Executed:
The deferred-content suite, the broader touched headless suite, the deferred-content sample build, the MVVM sample build, and the targeted leak validation passed.
Risk and Compatibility Notes
InitialDelayor per-hostDelayvalues can intentionally leave a host blank until its scheduled turn.Suggested PR Description
This PR finishes Dock’s deferred content materialization work by moving the hosts into a standalone
Dock.Controls.DeferredContentControlpackage, wiring the built-in themes to defer the main expensive content hosts, and adding configurable deferred timelines that can be scoped to any subtree. The package now supports shared or scoped queues, per-host delay and order, item-count or realization-time budgets, and presenter-contract hosts throughDeferredContentPresenter.The branch also adds a focused sample app and full docs for timeline scoping, budgets, and presenter-contract usage, plus follow-up fixes for queue starvation, bounded scans, delayed follow-up scheduling, leak cleanup, and presenter-sample reliability.