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
47 changes: 31 additions & 16 deletions docs/dock-active-document.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,47 @@
# Finding the Active Document

Only one document or tool can have focus at a time, even when multiple
`IDocumentDock` instances or floating windows are present. The
`IRootDock` that is currently active exposes its focused dockable via the
`FocusedDockable` property. You can subscribe to the
`IFactory.FocusedDockableChanged` event or query the property directly.
Only one dockable (document or tool) can have focus at a time, even when multiple
`IDocumentDock` instances or floating windows are present. The root dock that owns
the focused dockable stores it in `FocusedDockable`, and the factory reports focus
changes through `IFactory.FocusedDockableChanged`.

If you already have the root layout for a window, read `FocusedDockable` directly:

```csharp
var currentDocument = Layout?.FocusedDockable as IDocument;
```

To track focus across multiple windows, subscribe to the factory event and cache
the latest dockable:

```csharp
using System.Linq;
IDockable? focusedDockable = null;

factory.FocusedDockableChanged += (_, e) =>
Console.WriteLine($"Current document: {e.Dockable?.Title}");
{
focusedDockable = e.Dockable;
Console.WriteLine($"Focused dockable: {e.Dockable?.Title}");
};

var currentDocument = focusedDockable as IDocument;
```

var activeRoot = factory
.Find(d => d is IRootDock root && root.IsActive)
.OfType<IRootDock>()
.FirstOrDefault();
If you need the root dock that owns the focused dockable:

var current = activeRoot?.FocusedDockable as IDocument;
```csharp
var focusedRoot = focusedDockable is { } dockable
? factory.FindRoot(dockable, root => root.IsFocusableRoot)
: null;
```

```csharp
// Close whichever dockable currently has focus
if (activeRoot?.FocusedDockable is { } dockable)
// Close whichever dockable currently has focus.
if (focusedDockable is { } dockable)
{
factory.CloseDockable(dockable);
}
```

The focused dockable comes from the active root dock. When no document is focused,
`FocusedDockable` will be `null`.
The focused dockable can be `null` when nothing is focused. In multi-window
layouts, each root keeps its last focused dockable, so use the event to determine
the current focus.
14 changes: 7 additions & 7 deletions docs/dock-adapters.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Adapter Classes

Dock exposes a small set of helper classes that adapt the core interfaces to different host environments or provide additional runtime services. These adapters are mainly used by the sample applications but are part of the public API and can be reused in your own projects.
Dock exposes a small set of helper classes that adapt the core interfaces to different host environments or provide additional runtime services. The built-in model base classes (`DockWindow`, `DockBase`, `DockableBase`) wire these adapters in, so most applications interact with them through `IDock`, `IDockable`, and `IDockWindow`. You can use the adapters directly when implementing custom base types or host integrations.

## HostAdapter

`HostAdapter` implements `IHostAdapter` and bridges a dock window (`IDockWindow`) with a platform specific host window. It is responsible for presenting the floating window, updating its size and location and closing it when requested.
`HostAdapter` implements `IHostAdapter` and bridges a dock window (`IDockWindow`) with a platform-specific host window (`IHostWindow`). It resolves the host window via `IFactory.GetHostWindow` when needed, pushes layout, title, size, and position into the host when presenting, and reads the current size and position back when saving.

Typical usage looks like the following:

Expand All @@ -13,11 +13,11 @@ var hostAdapter = new HostAdapter(dockWindow);
hostAdapter.Present(isDialog: false);
```

The adapter fetches the window instance from `Factory.GetHostWindow` if it has not already been assigned. Calling `Save` stores the last known position and dimensions so that they can be restored later.
The adapter fetches the host window instance from `IFactory.GetHostWindow` if it has not already been assigned. Calling `Save` stores the last known position and dimensions so they can be restored later. `Exit` closes the host and clears the reference, and `SetActive` forwards the activation request to the host window.

## NavigateAdapter

`NavigateAdapter` implements `INavigateAdapter` and provides history based navigation for docks. It maintains two stacks for back and forward navigation and exposes methods such as `GoBack`, `GoForward` and `Navigate`.
`NavigateAdapter` implements `INavigateAdapter` and provides history-based navigation for docks. It maintains two stacks for back and forward navigation and exposes methods such as `GoBack`, `GoForward`, and `Navigate`. `Navigate` accepts an `IDockable`, a dockable ID (`string`), or `null` to reset navigation.

Create an instance for a given `IDock` and use it to issue navigation commands:

Expand All @@ -26,13 +26,13 @@ var adapter = new NavigateAdapter(rootDock);
adapter.Navigate(document, bSnapshot: true);
```

The adapter cooperates with the factory to activate dockables and can also show or close floating windows via `ShowWindows` and `ExitWindows`.
The adapter cooperates with the factory to activate dockables and can also show or close floating windows via `ShowWindows` and `ExitWindows`. The built-in `DockBase` types already use this adapter to implement `GoBack`, `GoForward`, `Navigate`, and `Close`.

## TrackingAdapter

`TrackingAdapter` stores bounds and pointer positions used when tools are pinned, visible or displayed as tabs. The docking logic reads these values when calculating drop targets or restoring the layout after a drag operation.
`TrackingAdapter` stores bounds and pointer positions used when dockables are visible, pinned, or displayed as tabs. It tracks both control-local and screen pointer positions. The docking logic reads these values when calculating drop targets or restoring the layout after a drag operation.

A new adapter contains `NaN` values for all coordinates until you call the various `Set*` methods. Most applications interact with `TrackingAdapter` indirectly through the built-in docking controls.
A new adapter contains `NaN` values for all coordinates until you call the various `Set*` methods. `DockableBase` uses it to implement the `IDockable` tracking methods, so most applications interact with `TrackingAdapter` indirectly through the built-in docking controls.

## When to use adapters

Expand Down
38 changes: 23 additions & 15 deletions docs/dock-advanced.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# Dock Advanced Guide

This guide highlights advanced features from the Dock samples. The API is shared across the MVVM, ReactiveUI and XAML versions so the same concepts apply no matter which approach you use.
This guide assumes you are familiar with the basics from the other guides. Interface descriptions are available in the [Dock API Reference](dock-reference.md). It focuses on runtime customization and advanced APIs.
This guide highlights advanced features from the Dock samples. The API is shared across the MVVM, ReactiveUI, and Avalonia/XAML model variants (plus INPC/Prism/Caliburn.Micro), so the same concepts apply no matter which approach you use. This guide assumes you are familiar with the basics from the other guides. Interface descriptions are available in the [Dock API Reference](dock-reference.md). It focuses on runtime customization and advanced APIs.

## Custom factories

All samples derive from `Factory` and override methods to configure the layout. In addition to `CreateLayout`, you can override:
Many samples derive from a `Factory` implementation and override methods to configure the layout. In addition to `CreateLayout`, you can override:

- `CreateWindowFrom` to customize new floating windows
- `CreateDocumentDock` to provide a custom `IDocumentDock` implementation
Expand Down Expand Up @@ -38,45 +37,54 @@ public override void InitLayout(IDockable layout)

## Handling events

`FactoryBase` exposes events for virtually every docking action. The samples subscribe to them to trace runtime changes:
`FactoryBase` exposes events for virtually every docking action. The samples subscribe to them to trace runtime changes.

Events are useful for hooking into your application's own logging or
analytics system. For example you might record when documents are opened
or closed so that the next run can restore them.

```csharp
factory.DockableAdded += (_, args) =>
{
Debug.WriteLine($"[DockableAdded] {args.Dockable?.Title}");
};
factory.ActiveDockableChanged += (_, args) =>
{
Debug.WriteLine($"[ActiveDockableChanged] {args.Dockable?.Title}");
};
factory.DockableAdded += (_, args) =>
factory.FocusedDockableChanged += (_, args) =>
{
Debug.WriteLine($"[DockableAdded] {args.Dockable?.Title}");
Debug.WriteLine($"[FocusedDockableChanged] {args.Dockable?.Title}");
};
```

// Example: track created and active documents
```csharp
factory.DockableAdded += (_, e) => Console.WriteLine($"Added {e.Dockable?.Id}");
factory.ActiveDockableChanged += (_, e) => Console.WriteLine($"Active {e.Dockable?.Id}");
```

You can react to focus changes, window moves or when dockables are pinned and unpinned.

## Saving and loading layouts

The XAML sample demonstrates persisting layouts with `DockSerializer`:
The XAML sample demonstrates persisting layouts with an `IDockSerializer` implementation:

```csharp
await using var stream = await file.OpenReadAsync();
var layout = _serializer.Load<IDock?>(stream);
if (layout is { })
{
dock.Layout = layout;
dock.Factory?.InitLayout(layout);
_dockState.Restore(layout);
}
```

When saving, call `DockState.Save` before serializing the layout:

```csharp
if (dock.Layout is { } layout)
{
_dockState.Save(layout);
_serializer.Save(stream, layout);
}
```

`DockState` tracks document/tool content (and document templates) so that state can be restored after loading.

## Dynamic documents and tools
Expand All @@ -98,7 +106,7 @@ activated automatically.

### Modern ItemsSource approach

For more efficient document management, use the ItemsSource property to bind directly to data collections:
For more efficient document management in the Avalonia model, use `DocumentDock.ItemsSource` to bind directly to data collections:

```csharp
// In your ViewModel
Expand All @@ -120,7 +128,7 @@ With XAML binding:
</DocumentDock>
```

This approach automatically creates and removes documents as the collection changes, provides automatic title mapping from properties like `Title` or `Name`, and integrates seamlessly with MVVM patterns. See the [DocumentDock ItemsSource guide](dock-itemssource.md) for comprehensive details.
This approach automatically creates and removes documents as the collection changes, provides automatic title mapping from properties like `Title` or `Name`, and integrates seamlessly with MVVM patterns. `ItemsSource` requires a `DocumentTemplate`; without one, documents are not generated. See the [DocumentDock ItemsSource guide](dock-itemssource.md) for comprehensive details.

Drag-and-drop handlers and file dialogs are used to open and save documents on the fly.

Expand Down
42 changes: 23 additions & 19 deletions docs/dock-api-scenarios.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
# Dock API Scenarios

This short document collects the most common ways the Dock API is used. The examples apply equally to the MVVM, ReactiveUI and XAML approaches.
This short document collects the most common ways the Dock API is used. The examples apply equally to the MVVM, ReactiveUI, and Avalonia/XAML model variants.

## Dock controls

Dock provides several themed controls that host parts of a layout.
Dock provides several themed controls that host parts of a layout. These are Avalonia controls used by the built-in themes and data templates.

### DockControl

The main control placed in your view to display a layout. Set the `Layout` and optionally the `Factory` properties from code or XAML:
The main control placed in your view to display a layout. Set the `Layout` and optionally the `Factory` properties from code or XAML:

```xaml
<DockControl Layout="{Binding Layout}"
Factory="{Binding Factory}"
InitializeLayout="True"
InitializeFactory="True">
<DockControl.Factory>
<Factory />
</DockControl.Factory>
</DockControl>
InitializeFactory="True" />
```

### DocumentDockControl

Displayed inside a `DocumentDock` to render documents. The control automatically binds to the active document and shows the `DocumentControl` template.
Default Avalonia control for `IDocumentDock`. The theme template switches between `DocumentControl` (tabbed) and `MdiDocumentControl` (MDI) based on `LayoutMode`.

### ToolDockControl

Used within a `ToolDock` to host tools or sidebars. This control handles layout grip behavior and interacts with `PinnedDockControl` when tools are pinned or auto-hidden.
Default Avalonia control for `IToolDock`. The theme template hosts `ToolChromeControl` and `ToolControl` to render the active tool and chrome buttons.

## Factories

All runtime operations go through a factory. Pick the implementation that matches your project style:
All runtime operations go through a factory. Pick the implementation that matches your project style:

### `Dock.Model.Avalonia.Factory`

Expand All @@ -42,7 +39,9 @@ Provides base classes implementing `INotifyPropertyChanged`. Ideal for traditio

### `Dock.Model.ReactiveUI.Factory`

Wraps the same API with ReactiveUI types. Commands become `ReactiveCommand` and properties derive from `ReactiveObject`.
Wraps the same API with ReactiveUI types. Commands become `ReactiveCommand` and properties derive from `ReactiveObject`.

Other packages expose the same factory pattern, including `Dock.Model.Prism.Factory`, `Dock.Model.CaliburMicro.Factory`, `Dock.Model.Inpc.Factory`, and `Dock.Model.ReactiveProperty.Factory`.

A typical initialization sequence looks like:

Expand All @@ -60,23 +59,28 @@ Use the MVVM, ReactiveUI or XAML samples as references for complete implementati

### Saving and restoring a layout

Layouts can be persisted using `DockSerializer` from the `Dock.Serializer.Newtonsoft` package.
Binary serialization is available via `ProtobufDockSerializer` in
the `Dock.Serializer.Protobuf` package.
Layouts can be persisted using any `IDockSerializer` implementation such as:
`Dock.Serializer.Newtonsoft`, `Dock.Serializer.SystemTextJson`,
`Dock.Serializer.Protobuf`, `Dock.Serializer.Xml`, or `Dock.Serializer.Yaml`.
This allows users to keep their preferred window arrangement between sessions.

```csharp
await using var stream = File.Create(path);
_dockState.Save(layout);
_serializer.Save(stream, layout);
```

Likewise you can restore a saved layout and reinitialise the factory:
Likewise you can restore a saved layout and reinitialize the factory and dock state:

```csharp
await using var stream = File.OpenRead(path);
var layout = _serializer.Load<IDock>(stream);
factory.InitLayout(layout);
dockControl.Layout = layout;
var layout = _serializer.Load<IDock?>(stream);
if (layout is { })
{
dockControl.Layout = layout;
dockControl.Factory?.InitLayout(layout);
_dockState.Restore(layout);
}
```

For an overview of all guides see the [documentation index](README.md).
20 changes: 15 additions & 5 deletions docs/dock-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ the [deep dive](dock-deep-dive.md).
property that stops docking tools together when their fixed sizes clash.
It also enforces docking group restrictions to control which dockables
can be docked together.
- **DockService** – Performs the actual move/split/swap operations on docks and
dockables and calls into the factory to update view models.
- **DockControlState** – Tracks pointer interactions and validates potential
drop targets using `DockManager`.
- **Factories** – Build and initialize dock view models. They expose
commands for runtime operations such as adding or closing documents.
- **DockSerializer** – Loads and saves layouts to disk so user changes can
- **DockState** – Captures document/tool content and templates that are not
serialized with the layout so they can be restored after loading.
- **IDockSerializer** – Loads and saves layouts to disk so user changes can
be persisted across sessions.

## Interaction overview
Expand All @@ -27,23 +31,29 @@ flowchart TD
UI[DockControl]
State[DockControlState]
Manager[DockManager]
Service[DockService]
Factory[IFactory / FactoryBase]
VM[Dock view models]
Serializer[DockSerializer]
Serializer[IDockSerializer]
StateStore[DockState]

UI --> State
State --> Manager
Manager --> Factory
Manager --> Service
Service --> Factory
Factory --> VM
UI --> VM
Serializer <--> VM
StateStore <--> VM
```

1. `DockControl` displays the layout created by the factory.
2. Pointer events are processed by `DockControlState` which consults
`DockManager` to validate docking actions.
3. `DockManager` modifies the view models by invoking factory methods.
4. `DockSerializer` can load or save the view models using the configured serializer (JSON, XML, YAML, or binary).
3. `DockManager` delegates to `DockService`, which calls factory methods to
update the view models.
4. `IDockSerializer` loads or saves the layout model, while `DockState` captures
non-serialized content and templates so they can be restored after loading.

Customising any of these pieces lets you extend Dock with application specific
behavior. For an in-depth walkthrough see the [deep dive](dock-deep-dive.md).
6 changes: 5 additions & 1 deletion docs/dock-caliburn-micro.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Optional packages for serialization:
```bash
dotnet add package Dock.Serializer.Newtonsoft # JSON (Newtonsoft.Json)
dotnet add package Dock.Serializer.SystemTextJson # JSON (System.Text.Json)
dotnet add package Dock.Serializer.Protobuf # Binary (protobuf-net)
dotnet add package Dock.Serializer.Xml # XML
dotnet add package Dock.Serializer.Yaml # YAML
```

## View location
Expand Down Expand Up @@ -156,7 +159,6 @@ public sealed class MainViewModel
{
Factory = new DockFactory();
Layout = Factory.CreateLayout();
Factory.InitLayout(Layout);
}

public IFactory Factory { get; }
Expand All @@ -171,6 +173,8 @@ public sealed class MainViewModel
InitializeLayout="True" />
```

If you call `Factory.InitLayout` manually, set `InitializeLayout="False"` to avoid double initialization.

## Notes

- The Caliburn.Micro model uses the same `IFactory` API and dockable interfaces as MVVM and ReactiveUI.
Expand Down
Loading