Skip to content
Merged
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: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,4 @@ docfx/api/*.yml
docfx/api/*.manifest

/plan
/report
2 changes: 2 additions & 0 deletions Dock.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<Project Path="samples/DockCodeOnlyMvvmSample/DockCodeOnlyMvvmSample.csproj" />
<Project Path="samples/DockCodeOnlySample/DockCodeOnlySample.csproj" />
<Project Path="samples/DockControlPanelsSample/DockControlPanelsSample.csproj" />
<Project Path="samples/DockDeferredContentSample/DockDeferredContentSample.csproj" />
<Project Path="samples/DockExternalTabStripsSample/DockExternalTabStripsSample.csproj" />
<Project Path="samples/DockOfficeSample/DockOfficeSample.csproj" />
<Project Path="samples/DockFigmaSample/DockFigmaSample.csproj" />
Expand Down Expand Up @@ -81,6 +82,7 @@
<Project Path="src/Dock.Avalonia.Themes.Fluent/Dock.Avalonia.Themes.Fluent.csproj" />
<Project Path="src/Dock.Avalonia.Themes.Simple/Dock.Avalonia.Themes.Simple.csproj" />
<Project Path="src/Dock.Avalonia/Dock.Avalonia.csproj" />
<Project Path="src/Dock.Controls.DeferredContentControl/Dock.Controls.DeferredContentControl.csproj" />
<Project Path="src/Dock.Controls.ProportionalStackPanel/Dock.Controls.ProportionalStackPanel.csproj" />
<Project Path="src/Dock.Controls.Recycling.Model/Dock.Controls.Recycling.Model.csproj" />
<Project Path="src/Dock.Controls.Recycling/Dock.Controls.Recycling.csproj" />
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ A docking layout system.
- **ItemsSource Support**: Bind document collections directly to DocumentDock for automatic document management
- **Flexible Content Templates**: Use DocumentTemplate for customizable document content rendering
- **Optional Document Content Caching**: Keep document views alive across tab switches via theme option (`CacheDocumentTabContent`)
- **Deferred Content Materialization**: Defer expensive content presenter work with shared or scoped timelines, per-host delay, and explicit ordering
- **Multiple MVVM Frameworks**: Support for ReactiveUI, Prism, ReactiveProperty, and standard MVVM patterns
- **Comprehensive Serialization**: Save and restore layouts with multiple format options (JSON, XML, YAML, Protobuf)
- **Rich Theming**: Fluent and Simple themes with full customization support
Expand Down Expand Up @@ -63,6 +64,7 @@ Install-Package Dock.Model.Mvvm
Install-Package Dock.Serializer.Newtonsoft
Install-Package Dock.Avalonia.Themes.Fluent
Install-Package Dock.Avalonia.Themes.Browser
Install-Package Dock.Controls.DeferredContentControl
```

**Available NuGet packages:**
Expand All @@ -74,6 +76,7 @@ Install-Package Dock.Avalonia.Themes.Browser
| [![NuGet](https://img.shields.io/nuget/v/Dock.Avalonia.Themes.Fluent.svg)](https://www.nuget.org/packages/Dock.Avalonia.Themes.Fluent) | [`Dock.Avalonia.Themes.Fluent`](https://www.nuget.org/packages/Dock.Avalonia.Themes.Fluent) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Avalonia.Themes.Fluent.svg)](https://www.nuget.org/packages/Dock.Avalonia.Themes.Fluent) |
| [![NuGet](https://img.shields.io/nuget/v/Dock.Avalonia.Themes.Browser.svg)](https://www.nuget.org/packages/Dock.Avalonia.Themes.Browser) | [`Dock.Avalonia.Themes.Browser`](https://www.nuget.org/packages/Dock.Avalonia.Themes.Browser) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Avalonia.Themes.Browser.svg)](https://www.nuget.org/packages/Dock.Avalonia.Themes.Browser) |
| [![NuGet](https://img.shields.io/nuget/v/Dock.Avalonia.Themes.Simple.svg)](https://www.nuget.org/packages/Dock.Avalonia.Themes.Simple) | [`Dock.Avalonia.Themes.Simple`](https://www.nuget.org/packages/Dock.Avalonia.Themes.Simple) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Avalonia.Themes.Simple.svg)](https://www.nuget.org/packages/Dock.Avalonia.Themes.Simple) |
| [![NuGet](https://img.shields.io/nuget/v/Dock.Controls.DeferredContentControl.svg)](https://www.nuget.org/packages/Dock.Controls.DeferredContentControl) | [`Dock.Controls.DeferredContentControl`](https://www.nuget.org/packages/Dock.Controls.DeferredContentControl) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Controls.DeferredContentControl.svg)](https://www.nuget.org/packages/Dock.Controls.DeferredContentControl) |
| [![NuGet](https://img.shields.io/nuget/v/Dock.Controls.ProportionalStackPanel.svg)](https://www.nuget.org/packages/Dock.Controls.ProportionalStackPanel) | [`Dock.Controls.ProportionalStackPanel`](https://www.nuget.org/packages/Dock.Controls.ProportionalStackPanel) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Controls.ProportionalStackPanel.svg)](https://www.nuget.org/packages/Dock.Controls.ProportionalStackPanel) |
| [![NuGet](https://img.shields.io/nuget/v/Dock.Controls.Recycling.svg)](https://www.nuget.org/packages/Dock.Controls.Recycling) | [`Dock.Controls.Recycling`](https://www.nuget.org/packages/Dock.Controls.Recycling) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Controls.Recycling.svg)](https://www.nuget.org/packages/Dock.Controls.Recycling) |
| [![NuGet](https://img.shields.io/nuget/v/Dock.Controls.Recycling.Model.svg)](https://www.nuget.org/packages/Dock.Controls.Recycling.Model) | [`Dock.Controls.Recycling.Model`](https://www.nuget.org/packages/Dock.Controls.Recycling.Model) | [![Downloads](https://img.shields.io/nuget/dt/Dock.Controls.Recycling.Model.svg)](https://www.nuget.org/packages/Dock.Controls.Recycling.Model) |
Expand Down Expand Up @@ -126,6 +129,7 @@ Install-Package Dock.Avalonia.Themes.Browser -Pre
- **`DockXamlSample`** - XAML layouts with ItemsSource examples
- **`DockMvvmSample`** - Full MVVM implementation
- **`DockReactiveUISample`** - ReactiveUI patterns
- **`DockDeferredContentSample`** - Deferred timeline scopes, delay, order, and presenter-host behavior
- **`DockOfficeSample`** - Office-style workspaces with ReactiveUI navigation
- **`DockCodeOnlySample`** - Pure C# layouts
- **`Notepad`** - Real-world text editor example
Expand Down
3 changes: 2 additions & 1 deletion docfx/articles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Dock supports a wide range of UI patterns:
- Floating windows, docking targets, and drag gestures.
- Layout persistence and restoration.
- Custom themes and styling.
- Control recycling for performance.
- Control recycling and deferred content presentation for performance.
- Overlay systems for busy states and dialogs.

## How to approach the documentation
Expand All @@ -65,6 +65,7 @@ Sample apps demonstrate complete layouts and patterns:
Dock is published on NuGet. Common packages include:

- `Dock.Avalonia`
- `Dock.Controls.DeferredContentControl`
- `Dock.Model`
- `Dock.Avalonia.Themes.Fluent`
- `Dock.Avalonia.Themes.Browser`
Expand Down
6 changes: 6 additions & 0 deletions docfx/articles/dock-custom-theme.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ or copy them into your own assembly and include `/Controls/...` resources.
</Styles>
```

If your copied templates host expensive document or tool content, add the
`Dock.Controls.DeferredContentControl` package and replace eager content hosts
with `DeferredContentControl` or `DeferredContentPresenter`. See the
[Deferred content presentation](dock-deferred-content.md) guide for the theme
patterns used by the built-in Dock themes.

## 3. Apply the theme

Reference the theme from `App.axaml`:
Expand Down
33 changes: 33 additions & 0 deletions docfx/articles/dock-deferred-content-sample.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Deferred Content Sample

The deferred content sample is a focused app for `Dock.Controls.DeferredContentControl`.

## Sample project

- `samples/DockDeferredContentSample/DockDeferredContentSample.csproj`
- `samples/DockDeferredContentSample/MainWindow.axaml`

## What it demonstrates

- The shared default timeline configured through `DeferredContentPresentationSettings`.
- A scoped `DeferredContentPresentationTimeline` applied with `DeferredContentScheduling.Timeline`.
- Per-host `DeferredContentScheduling.Delay`.
- Per-host `DeferredContentScheduling.Order`.
- `DeferredContentControl`.
- `DeferredContentPresenter`.
- A templated presenter-contract host that feeds `DeferredContentPresenter` through template bindings.
- Count-based and realization-time budgets.

## Run the sample

```bash
dotnet run --project samples/DockDeferredContentSample/DockDeferredContentSample.csproj
```

## What to validate

- The first group uses the shared default queue.
- The second group realizes in scoped order with per-host delays.
- The third group keeps a `ContentPresenter` contract while using a scoped time-budgeted timeline.
- The first presenter card in the third group appears immediately, while the later cards still stage through the scoped `Delay` and `Order` values.
- The three sections can behave differently because they do not share one queue.
229 changes: 229 additions & 0 deletions docfx/articles/dock-deferred-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Deferred Content Presentation

Dock can defer expensive content materialization so presenter-heavy layouts do not pay for template creation, logical attachment, and style activation inside the first measure burst.

The feature lives in the `Dock.Controls.DeferredContentControl` package and exposes:

- `DeferredContentControl` for templates that can use a `ContentControl`.
- `DeferredContentPresenter` for templates that must keep a `ContentPresenter` contract.
- `DeferredContentPresentationTimeline` for shared scoped queues.
- `DeferredContentScheduling` for inherited `Timeline`, `Delay`, and `Order` attached properties.

## Add the package

```bash
dotnet add package Dock.Controls.DeferredContentControl
```

## Default behavior

If you do nothing beyond replacing an eager content host with `DeferredContentControl` or `DeferredContentPresenter`, the package keeps the current default behavior:

- a shared default queue,
- next-pass presentation,
- FIFO ordering for equal items,
- count-based batching through `DeferredContentPresentationSettings`,
- a short opacity reveal to smooth later deferred content swaps without hiding first paint.

That keeps existing Dock theme behavior unchanged.

## Use DeferredContentControl

Use `DeferredContentControl` in theme templates that do not require a `ContentPresenter`-typed part:

```xaml
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dcc="clr-namespace:Dock.Controls.DeferredContentControl;assembly=Dock.Controls.DeferredContentControl">
<ControlTemplate x:Key="MyDocumentTemplate" TargetType="DocumentControl">
<dcc:DeferredContentControl x:Name="PART_ContentPresenter"
Content="{Binding}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<dcc:DeferredContentControl.ContentTemplate>
<ControlRecyclingDataTemplate Parent="{Binding #PART_ContentPresenter}" />
</dcc:DeferredContentControl.ContentTemplate>
</dcc:DeferredContentControl>
</ControlTemplate>
</ResourceDictionary>
```

The host keeps the latest `Content` and `ContentTemplate`, then forwards them to its inner presenter when the deferred queue grants that target a turn.

## Use DeferredContentPresenter

Some templates must keep a `ContentPresenter` contract. In that case, use `DeferredContentPresenter` directly:

```xaml
<dcc:DeferredContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
```

This is the right choice for host windows, chrome templates, or any control contract that names the part as a `ContentPresenter`.

## Configure the default timeline

`DeferredContentPresentationSettings` now configures the shared default timeline:

```csharp
DeferredContentPresentationSettings.BudgetMode = DeferredContentPresentationBudgetMode.ItemCount;
DeferredContentPresentationSettings.MaxPresentationsPerPass = 3;
DeferredContentPresentationSettings.InitialDelay = TimeSpan.Zero;
DeferredContentPresentationSettings.FollowUpDelay = TimeSpan.FromMilliseconds(16);
DeferredContentPresentationSettings.RevealDuration = TimeSpan.FromMilliseconds(90);
```

For a realization-time budget:

```csharp
DeferredContentPresentationSettings.BudgetMode = DeferredContentPresentationBudgetMode.RealizationTime;
DeferredContentPresentationSettings.MaxRealizationTimePerPass = TimeSpan.FromMilliseconds(10);
DeferredContentPresentationSettings.FollowUpDelay = TimeSpan.FromMilliseconds(33);
DeferredContentPresentationSettings.RevealDuration = TimeSpan.FromMilliseconds(90);
```

Properties:

- `BudgetMode`
- `MaxPresentationsPerPass`
- `MaxRealizationTimePerPass`
- `InitialDelay`
- `FollowUpDelay`
- `RevealDuration`
- `DefaultTimeline`

Set `RevealDuration` to `TimeSpan.Zero` to disable the reveal transition.

## Scope a timeline to a subtree

Create a `DeferredContentPresentationTimeline` resource and attach it to a container with `DeferredContentScheduling.Timeline`.

```xaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dcc="clr-namespace:Dock.Controls.DeferredContentControl;assembly=Dock.Controls.DeferredContentControl">
<Window.Resources>
<dcc:DeferredContentPresentationTimeline x:Key="OrderedTimeline"
MaxPresentationsPerPass="1"
FollowUpDelay="0:0:0.10" />
</Window.Resources>

<StackPanel dcc:DeferredContentScheduling.Timeline="{StaticResource OrderedTimeline}">
<dcc:DeferredContentControl Content="{Binding PrimaryDocument}" />
<dcc:DeferredContentControl Content="{Binding SecondaryDocument}" />
</StackPanel>
</Window>
```

Every deferred host in that subtree shares the same scoped queue. A different subtree can attach a different timeline and realize independently.

## Set per-host delay and order

`DeferredContentScheduling.Delay` and `DeferredContentScheduling.Order` are inheritable attached properties. You can set them on a scope root or on individual hosts.

```xaml
<StackPanel dcc:DeferredContentScheduling.Timeline="{StaticResource OrderedTimeline}">
<dcc:DeferredContentControl Content="{Binding First}"
dcc:DeferredContentScheduling.Order="-10"
dcc:DeferredContentScheduling.Delay="0:0:0.00" />

<dcc:DeferredContentControl Content="{Binding Second}"
dcc:DeferredContentScheduling.Order="5"
dcc:DeferredContentScheduling.Delay="0:0:0.04" />

<dcc:DeferredContentControl Content="{Binding Third}"
dcc:DeferredContentScheduling.Order="20"
dcc:DeferredContentScheduling.Delay="0:0:0.12" />
</StackPanel>
```

Rules:

- lower `Order` values realize first,
- equal `Order` values preserve FIFO behavior,
- `Delay` adds per-host time on top of the timeline's `InitialDelay`,
- scoped order and delay work in both count-based and time-based budgets.

If you configure a non-zero `InitialDelay` or large per-host `Delay` values, a host can legitimately remain blank until its scheduled turn. Use smaller delays, or stage visible placeholder UI outside the deferred host, when first-paint completeness matters.

## How scopes and budgets interact

Every `DeferredContentPresentationTimeline` owns a separate queue and scheduler. That means you can mix different policies in one app:

- one subtree can use `ItemCount` batching,
- another subtree can use `RealizationTime` batching,
- each subtree can use different `InitialDelay` and `FollowUpDelay`,
- per-host `Delay` and `Order` still apply inside each scope.

This makes deferred loading composable in the same way Dock scopes control recycling.

## Smooth the deferred reveal

Deferred loading can still cause a visible pop when content arrives on a later dispatcher turn. The package smooths later deferred content swaps by fading the presenter in over `RevealDuration`.

The first realization from a blank host stays immediate so startup and structural dock hosts are not hidden behind an extra opacity handoff.

That improves the perceived refresh, but it does not change measurement correctness. If a host is auto-sized and its true size is unknown until the content is materialized, layout can still change when the real content arrives. In those cases, use one or more of:

- explicit width or height on the deferred host,
- placeholder chrome outside the deferred host,
- smaller timeline delays so the content lands closer to the initial layout burst.

## Opt out for specific content

If a content object must stay synchronous, implement `IDeferredContentPresentation` and return `false`.

```csharp
public sealed class ManagedDockWindowDocument : IDeferredContentPresentation
{
bool IDeferredContentPresentation.DeferContentPresentation => false;
}
```

Dock uses this for managed floating-window content that should not be delayed.

## Built-in Dock theme behavior

The Fluent and Simple Dock themes already use deferred presentation for the heavy content hosts:

- document content,
- tool content,
- document and tool active presenters,
- MDI document content,
- split-view content,
- pinned content,
- root and dock-level active hosts,
- host-window and chrome presenter paths.

Cached document tab content stays eager by design because that path intentionally prebuilds hidden tabs.

## Sample

The repository includes a focused sample that demonstrates:

- the shared default timeline,
- scoped timelines,
- inherited `Delay`,
- inherited `Order`,
- `DeferredContentControl`,
- `DeferredContentPresenter` through a presenter-contract templated host,
- count-based and time-based budget modes.

See [Deferred content sample](dock-deferred-content-sample.md).

Run it with:

```bash
dotnet run --project samples/DockDeferredContentSample/DockDeferredContentSample.csproj
```

## Related guides

- [Deferred content sample](dock-deferred-content-sample.md)
- [Control recycling](dock-control-recycling.md)
- [Custom Dock themes](dock-custom-theme.md)
- [Styling and theming](dock-styling.md)
4 changes: 4 additions & 0 deletions docfx/articles/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@
href: dock-overlay-customization.md
- name: Selector overlay
href: dock-selector-overlay.md
- name: Deferred content presentation
href: dock-deferred-content.md
- name: Deferred content sample
href: dock-deferred-content-sample.md
- name: Control recycling
href: dock-control-recycling.md
- name: Proportional StackPanel
Expand Down
Loading
Loading