好,阿杰 — 我把 AvalonDock(WPF 停靠/浮动窗口库) 的基础用法、常见场景与示例代码整理成一份实战指南,覆盖从安装、XAML 布局、动态增删文档/工具窗口、MVVM 集成到布局持久化与主题定制。按步骤来,看完就能在 WPF 项目里快速上手。

AvalonDock 基本用法(速成指南)


1. 简介与安装

  • AvalonDock 是 WPF 的停靠(docking)/浮动(floating)窗口框架,常用于 IDE、管理后台那类可停靠面板的桌面应用。
  • 推荐使用 Xceed 维护的 NuGet 包:Xceed.Wpf.AvalonDock(注意不同 fork/版本 API 细节有差异)。

安装(NuGet):

Install-Package Xceed.Wpf.AvalonDock
# 可加主题包(若有)
# Install-Package Xceed.Wpf.AvalonDock.Themes

常见命名空间

using Xceed.Wpf.AvalonDock;
using Xceed.Wpf.AvalonDock.Layout;
using Xceed.Wpf.AvalonDock.Layout.Serialization;
using Xceed.Wpf.AvalonDock.Themes;


2. 核心概念

  • DockingManager:根控件,管理整个停靠布局与行为。
  • LayoutRoot:布局根(包含面板、文档区、锚定区等)。
  • LayoutDocument / LayoutDocumentPane:主要用于“文档”类型的窗口(像编辑器标签页)。
  • LayoutAnchorable / LayoutAnchorablePane:工具窗口(可停靠在四周、可折叠、浮动)。
  • XmlLayoutSerializer:序列化/反序列化布局(保存与还原用户自定义布局)。
  • Theme:主题(外观样式),例如 VS2013、Metro 等(依版本而异)。

3. XAML 基本布局示例

一个最小可用的布局,包含文档区域与左侧工具窗口:

<Window x:Class="Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ad="http://avalondock.codeplex.com"
        Title="AvalonDock Demo" Height="600" Width="900">
    <Grid>
        <ad:DockingManager x:Name="DockManager">
            <ad:DockingManager.Theme>
                <!-- 根据包版本使用可用的主题类 -->
                <ad:VS2013LightTheme />
            </ad:DockingManager.Theme>

            <ad:LayoutRoot>
                <ad:LayoutPanel Orientation="Horizontal">
                    <!-- 左侧工具区 -->
                    <ad:LayoutAnchorablePaneGroup DockWidth="200">
                        <ad:LayoutAnchorablePane>
                            <ad:LayoutAnchorable Title="工具" CanClose="False">
                                <TextBlock Text="工具窗口内容" Padding="10"/>
                            </ad:LayoutAnchorable>
                        </ad:LayoutAnchorablePane>
                    </ad:LayoutAnchorablePaneGroup>

                    <!-- 主文档区 -->
                    <ad:LayoutDocumentPane x:Name="DocumentPane">
                        <ad:LayoutDocument Title="欢迎">
                            <TextBlock Text="这里是文档区域" Padding="10"/>
                        </ad:LayoutDocument>
                    </ad:LayoutDocumentPane>
                </ad:LayoutPanel>
            </ad:LayoutRoot>
        </ad:DockingManager>
    </Grid>
</Window>

注意:不同版本的 AvalonDock XML 命名空间与 Theme 类名可能略有差异,上例中 VS2013LightTheme 仅作示例,按你的包文档选择实际可用的主题类。


4. 在代码里动态创建/添加文档与工具窗口

推荐做法:在 XAML 中预定义 LayoutDocumentPane(如上例的 x:Name="DocumentPane"),在代码里直接向它添加子项。

C# 示例(窗口 code-behind):

using Xceed.Wpf.AvalonDock.Layout;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void AddDocument(string title, UIElement content)
    {
        var doc = new LayoutDocument { Title = title, Content = content };
        DocumentPane.Children.Add(doc);
        doc.IsActive = true;
    }

    private void AddToolWindow(string title, UIElement content)
    {
        var anchorable = new LayoutAnchorable { Title = title, Content = content, CanClose = true };
        // 把工具窗口加入到第一个 AnchorablePane(这里简单示例)
        var anchorPane = DockManager.Layout.RootPanel.Children
                           .OfType<LayoutAnchorablePaneGroup>()
                           .SelectMany(g => g.Children)
                           .OfType<LayoutAnchorablePane>()
                           .FirstOrDefault();

        if (anchorPane != null)
            anchorPane.Children.Add(anchorable);
        else
        {
            // 若没有则创建一个新的锚定面板并插入布局(更复杂的场景需要更精细的布局操作)
        }
        anchorable.IsActive = true;
    }
}

更稳妥的做法是预先在 XAML 中定义命名的 LayoutAnchorablePane,然后直接在 code-behind 使用该命名控制引用,避免复杂的 LayoutRoot 操作。


5. MVVM 风格(视图模型绑定)

通常不建议直接把视图对象作为 LayoutDocument.Content,应该把 ViewModel 绑定到 LayoutDocument,并用 DataTemplate 映射 ViewModel 到 View。

示例:

ViewModels:

public class DocumentViewModel
{
    public string Title { get; set; }
    public string Text { get; set; }
}

XAML DataTemplate & Layout(简化):

<Window.Resources>
    <DataTemplate DataType="{x:Type vm:DocumentViewModel}">
        <TextBox Text="{Binding Text}" AcceptsReturn="True" />
    </DataTemplate>
</Window.Resources>

<!-- 在代码里创建 LayoutDocument 并把 ViewModel 作为 Content -->

在 code-behind / ViewModel layer:

var vm = new DocumentViewModel { Title = "Doc1", Text = "..." };
var doc = new LayoutDocument { Title = vm.Title, Content = vm };
DocumentPane.Children.Add(doc);

AvalonDock 会使用 DataTemplate 把 DocumentViewModel 渲染成 TextBox。这允许遵循 MVVM,不直接把 UI 元素塞入布局。


6. 布局持久化:保存与还原用户布局

使用 XmlLayoutSerializer 保存当前布局到 XML 文件,启动时读取恢复。

保存示例:

using (var stream = new StreamWriter("layout.config"))
{
    var serializer = new XmlLayoutSerializer(DockManager);
    serializer.Serialize(stream);
}

加载示例(启动时):

if (File.Exists("layout.config"))
{
    var serializer = new XmlLayoutSerializer(DockManager);
    using (var stream = new StreamReader("layout.config"))
    {
        serializer.Deserialize(stream);
    }
}

注意事项

  • 反序列化时,如果 Layout 中引用了 Content(视图或 ViewModel)需要提供 Deserialize 的回调把占位符内容替换成实际对象。XmlLayoutSerializer 支持事件/回调(例如 LayoutSerializationCallback),用于在反序列化时注入实际内容:
var serializer = new XmlLayoutSerializer(DockManager);
serializer.LayoutSerializationCallback += (s, e) =>
{
    if (e.Model is LayoutDocument docModel)
    {
        // 根据 docModel.ContentId 或 Title 创建并赋值 Content(ViewModel 或 View)
        e.Content = CreateContentFor(docModel.ContentId);
    }
};
serializer.Deserialize("layout.config");

  • 如果你把 ViewModel(可序列化的 ID)作为 Content,反序列化时利用 ID 恢复它会比较稳妥。

7. 主题与样式

AvalonDock 支持多种主题(取决于包),常见有 VS2013、Aero、Metro 等。设置方式示例:

DockManager.Theme = new VS2013LightTheme(); // 或 VS2013BlueTheme / MetroTheme 等(依版本)

或在 XAML 中声明 Theme 节点(参考包文档示例)。


8. 常见事件与 API(快速参考)

  • LayoutDocument.Closing:文档关闭事件(可用于阻止关闭、提示保存)。
  • DockingManager.ActiveContent:当前激活的内容。
  • LayoutDocument.IsActive / LayoutAnchorable.IsActive:设为 true 可激活对应面板。
  • LayoutDocumentPane.Children / LayoutAnchorablePane.Children:添加/移除子项。

9. 常见问题与最佳实践

  • 不要在反序列化时直接依赖 UI 元素。用 ContentId 或 ViewModel ID,在 LayoutSerializationCallback 中动态创建内容。
  • MVVM:把 Document/Anchorable 的 Content 指向 ViewModel,并使用 DataTemplate 映射为 View。
  • 命名与 ContentId:在序列化前为每个文档/工具窗口设置唯一 ContentId,便于恢复。
  • 并发/线程:所有 AvalonDock 操作必须在 UI 线程(Dispatcher)中执行。
  • 布局自定义:复杂动态布局建议先在 XAML 里预置容器(DocumentPane、AnchorablePane),在运行时只做添加/移除,避免频繁修改 LayoutRoot 结构。
  • 主题兼容:不同 AvalonDock 版本主题类名不同,按你安装的包来选择主题类。

10. 典型用例:基于 MVVM 的最小运行流程

  1. 在 XAML 定义 DockingManagerLayoutDocumentPane(命名)和 DataTemplate(把 ViewModel 映射成 View)。
  2. 在主 VM 中维护 ObservableCollection<DocumentViewModel>
  3. 当用户打开一个新文件时,创建 DocumentViewModel 并在主 VM(或 code-behind)中创建 LayoutDocument 并把 ViewModel 赋给 Content 并加入 DocumentPane.Children
  4. 保存布局时序列化;重启加载时利用 LayoutSerializationCallback 恢复 ViewModel -> Content。

11. 小 Demo(关键片段汇总)

XAML(含 DataTemplate 与 DocumentPane):

&lt;ad:DockingManager x:Name="DockManager">
    &lt;ad:LayoutRoot>
        &lt;ad:LayoutPanel Orientation="Horizontal">
            &lt;ad:LayoutAnchorablePaneGroup DockWidth="200">
                &lt;ad:LayoutAnchorablePane x:Name="ToolPane" />
            &lt;/ad:LayoutAnchorablePaneGroup>

            &lt;ad:LayoutDocumentPane x:Name="DocumentPane" />
        &lt;/ad:LayoutPanel>
    &lt;/ad:LayoutRoot>
&lt;/ad:DockingManager>

&lt;Window.Resources>
    &lt;DataTemplate DataType="{x:Type vm:DocumentViewModel}">
        &lt;local:DocumentView />   &lt;!-- DocumentView 是展示该 VM 的 UserControl -->
    &lt;/DataTemplate>
&lt;/Window.Resources>

创建文档(Code-behind / ViewModel 层触发)

var vm = new DocumentViewModel { Title = "New" };
var doc = new LayoutDocument { Title = vm.Title, ContentId = Guid.NewGuid().ToString(), Content = vm };
DocumentPane.Children.Add(doc);
doc.IsActive = true;

序列化回调(反序列化时恢复 Content)

var serializer = new XmlLayoutSerializer(DockManager);
serializer.LayoutSerializationCallback += (s, e) =>
{
    if (e.Model is LayoutDocument docModel)
    {
        // 以 ContentId 恢复 ViewModel
        e.Content = MyViewModelFactory.CreateForContentId(docModel.ContentId);
    }
};
serializer.Deserialize("layout.config");


12. 想要我帮你做什么?(可选交付)

我可以帮你进一步交付下面任意一项:

  • ✅ 给你生成一个完整的 可运行 WPF 示例工程(MVVM、AvalonDock、布局持久化、主题切换)并打包成 zip。
  • ✅ 把关键步骤写成 PDF Cheatsheet(包含常用 API、保存/加载布局代码、MVVM 示例)。
  • ✅ 或只给你一个最简 Repository 的 README + 代码片段,方便直接 clone 并运行。

告诉我你要哪一个(或直接回复“示例工程”),我就直接生成并把下载链接给你。

好 👌 阿杰,那我给你准备 AvalonDock 学习大礼包,包括:

  1. PDF 速查表 📑
    • 安装与引用方式
    • 核心概念总结(DockingManager、LayoutRoot、Document/Anchorable 等)
    • 常用 XAML 模板
    • 动态添加/移除文档窗口与工具窗口的代码片段
    • MVVM + DataTemplate 示例
    • 布局保存与加载(XmlLayoutSerializer + 回调)
    • 常见坑点与最佳实践
  2. Markdown 版笔记 📘
    • 适合放在 Obsidian / Typora / GitHub Repo 里
    • 代码高亮 + 简化清单式说明
  3. 完整可运行示例工程 (WPF + AvalonDock) 🛠️
    • MVVM 架构
    • 主窗口带 DockingManager,包含 DocumentPane + ToolPane
    • 动态添加文档、关闭文档
    • 工具窗口可停靠/浮动
    • 主题切换(VS2013 Light/Blue)
    • 布局保存与恢复(关闭应用保存,启动时加载)