This guide explains how to get started with the INPC (INotifyPropertyChanged) implementation of Dock. The INPC model provides basic property change notifications without the overhead of full MVVM command patterns, making it ideal for simpler scenarios or custom MVVM frameworks.
The Dock.Model.Inpc
package provides INotifyPropertyChanged
implementations without additional command support. The sample project DockInpcSample
in the repository demonstrates this approach. For interface details refer to the Dock API Reference.
💡 Modern Approach: For easier document management, consider using DocumentDock.ItemsSource which automatically creates and manages documents from collections. This approach is covered in detail in the Document and Tool Content Guide.
Follow these instructions to create a minimal INPC-based application using Dock.
Create a new Avalonia project
dotnet new avalonia.app -o MyDockApp
cd MyDockApp
Install the Dock packages
dotnet add package Dock.Avalonia
dotnet add package Dock.Model.Inpc
dotnet add package Dock.Avalonia.Themes.Fluent
Optional packages:
# For serialization (choose one):
dotnet add package Dock.Serializer.Newtonsoft # JSON (Newtonsoft.Json)
dotnet add package Dock.Serializer.SystemTextJson # JSON (System.Text.Json)
# For dependency injection:
dotnet add package Dock.Model.Extensions.DependencyInjection
Set up View Locator (Required)
INPC requires a view locator to map view models to their corresponding views. Choose one of the following approaches:
Option A: Static View Locator with Source Generators (Recommended)
Add the StaticViewLocator package:
dotnet add package StaticViewLocator
Create a ViewLocator.cs
file:
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Dock.Model.Core;
using StaticViewLocator;
namespace MyDockApp;
[StaticViewLocator]
public partial class ViewLocator : IDataTemplate
{
public Control? Build(object? data)
{
if (data is null)
return null;
var type = data.GetType();
if (s_views.TryGetValue(type, out var func))
return func.Invoke();
throw new Exception($"Unable to create view for type: {type}");
}
public bool Match(object? data)
{
return data is INotifyPropertyChanged || data is IDockable;
}
}
Option B: Convention-Based View Locator
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Dock.Model.Core;
namespace MyDockApp;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? data)
{
if (data is null)
return null;
var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null)
return (Control)Activator.CreateInstance(type)!;
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is INotifyPropertyChanged || data is IDockable;
}
}
Register the view locator in App.axaml
:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyDockApp"
x:Class="MyDockApp.App">
<Application.DataTemplates>
<local:ViewLocator />
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
<DockFluentTheme />
</Application.Styles>
</Application>
Create a factory and view models
Derive from Dock.Model.Inpc.Factory
and implement CreateLayout
. Your documents and tools should inherit from the INPC versions:
using Dock.Model.Core;
using Dock.Model.Inpc;
using Dock.Model.Inpc.Controls;
namespace MyDockApp.ViewModels;
public class DockFactory : Factory
{
public override IRootDock CreateLayout()
{
var doc = new DocumentViewModel { Id = "Doc1", Title = "Document" };
var tool = new ToolViewModel { Id = "Tool1", Title = "Tool1" };
var root = CreateRootDock();
root.VisibleDockables = CreateList<IDockable>(
new DocumentDock
{
VisibleDockables = CreateList<IDockable>(doc),
ActiveDockable = doc
},
new ToolDock
{
VisibleDockables = CreateList<IDockable>(tool),
ActiveDockable = tool
});
return root;
}
}
// Example document view model using INPC
public class DocumentViewModel : Document
{
private string _content = "Document content here...";
public string Content
{
get => _content;
set => SetProperty(ref _content, value);
}
}
// Example tool view model using INPC
public class ToolViewModel : Tool
{
private string _status = "Ready";
public string Status
{
get => _status;
set => SetProperty(ref _status, value);
}
}
Create views for your view models
Create corresponding views for your documents and tools:
DocumentView.axaml:
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyDockApp.Views.DocumentView">
<TextBox Text="{Binding Content}" AcceptsReturn="True" />
</UserControl>
ToolView.axaml:
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyDockApp.Views.ToolView">
<StackPanel>
<TextBlock Text="Tool Panel" FontWeight="Bold" />
<TextBlock Text="{Binding Status}" />
</StackPanel>
</UserControl>
Initialize the layout
Create and initialize the layout in your main window:
using Avalonia.Controls;
using Dock.Avalonia.Controls;
using MyDockApp.ViewModels;
namespace MyDockApp;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitializeDock();
}
private void InitializeDock()
{
var factory = new DockFactory();
var layout = factory.CreateLayout();
factory.InitLayout(layout);
// Assuming you have a DockControl named "Dock" in MainWindow.axaml
var dockControl = this.Find<DockControl>("Dock");
if (dockControl != null)
{
dockControl.Layout = layout;
}
}
}
And add a DockControl
to MainWindow.axaml
:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyDockApp.MainWindow"
Title="My Dock App">
<DockControl x:Name="Dock" />
</Window>
Run the application
dotnet run
When using Dock.Model.Inpc
, you need to:
Property Change Notifications: Your view models should inherit from the INPC base classes (Document
, Tool
, RootDock
, etc.) which implement INotifyPropertyChanged
.
View Locator: Always set up a view locator to map view models to views.
Factory Setup: Use Dock.Model.Inpc.Factory
as your base factory class.
Context and Dockable Locators: For serialization support, populate the ContextLocator
and DockableLocator
dictionaries in your factory’s InitLayout
method:
public override void InitLayout(IDockable layout)
{
ContextLocator = new Dictionary<string, Func<object?>>
{
["Doc1"] = () => new DocumentData(),
["Tool1"] = () => new ToolData()
};
DockableLocator = new Dictionary<string, Func<IDockable?>>
{
["Doc1"] = () => new DocumentViewModel { Id = "Doc1", Title = "Document" },
["Tool1"] = () => new ToolViewModel { Id = "Tool1", Title = "Tool1" }
};
base.InitLayout(layout);
}
The INPC implementation provides:
INotifyPropertyChanged
This makes it perfect for scenarios where you want the docking functionality without the overhead of a full MVVM framework, or when integrating with custom MVVM implementations.
For more advanced scenarios and command support, consider the MVVM guide or ReactiveUI guide.