动态添加 DockablePanes

Dynamically add DockablePanes

我有一个 CustomControl 和该控件的视图模型列表。使用 ItemsControl 我能够为每个视图模型动态创建一个控件。现在我使用 AvalonDock,我想为每个生成的 UserControl 添加一个 DockableContent。这如何动态完成?

您想将 collection 的 ViewModels 绑定到停靠管理器的 DocumentSource,并设置模板绑定到标题等属性,如下所示:

<dock:DockingManager DocumentsSource="{Binding Documents}" >
    <dock:DockingManager.LayoutItemContainerStyle>
        <Style TargetType="{x:Type dockctrl:LayoutItem}">
            <Setter Property="Title" Value="{Binding Model.Title}" />
            <Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}" />
            <Setter Property="CanClose" Value="{Binding Model.CanClose}" />
        </Style>
    </dock:DockingManager.LayoutItemContainerStyle>
</dock:DockingManager>

并使用如下方式动态添加内容:

Documents.Add(MyNewlyCreatedViewModel)

编辑: 当您使用静态窗格时,事情会变得更加复杂。您必须使用模板选择器为 normal/static 窗格选择正确的模板。 MVVM AvalonDock 示例非常好。以下是我的实现方式(工具是静态的):

    <avalonDock:DockingManager
        AnchorablesSource="{Binding Tools}" 
        DocumentsSource="{Binding Documents}"
        AllowMixedOrientation="True" >
        <avalonDock:DockingManager.Theme>
            <avalonDock:MetroTheme />
        </avalonDock:DockingManager.Theme>
        <avalonDock:DockingManager.LayoutUpdateStrategy>
            <helpers:LayoutUpdateStrategy />
        </avalonDock:DockingManager.LayoutUpdateStrategy>
        <avalonDock:DockingManager.LayoutItemContainerStyleSelector>
            <helpers:AutobinderLayoutSelector>
                <helpers:AutobinderLayoutSelector.DocumentStyle>
                    <Style TargetType="{x:Type avalonDock:LayoutItem}">
                        <Setter Property="Title" Value="{Binding Model.Title}" />
                    </Style>
                </helpers:AutobinderLayoutSelector.DocumentStyle>
                <helpers:AutobinderLayoutSelector.ToolStyle>
                    <Style TargetType="{x:Type avalonDock:LayoutItem}">
                        <Setter Property="Title" Value="{Binding Model.Title}" />
                        <Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
                    </Style>
                </helpers:AutobinderLayoutSelector.ToolStyle>
            </helpers:AutobinderLayoutSelector>
        </avalonDock:DockingManager.LayoutItemContainerStyleSelector>
        <avalonDock:DockingManager.LayoutItemTemplateSelector>
            <helpers:AutobinderTemplateSelector>
                <helpers:AutobinderTemplateSelector.DocumentTemplate>
                    <DataTemplate>
                        <ContentControl cal:View.Model="{Binding . }" IsTabStop="False" />
                    </DataTemplate>
                </helpers:AutobinderTemplateSelector.DocumentTemplate>
                <helpers:AutobinderTemplateSelector.ToolTemplate>
                    <DataTemplate>
                        <ContentControl cal:View.Model="{Binding . }" IsTabStop="False"  />
                    </DataTemplate>
                </helpers:AutobinderTemplateSelector.ToolTemplate>
            </helpers:AutobinderTemplateSelector>
        </avalonDock:DockingManager.LayoutItemTemplateSelector>

        <avalonDock:LayoutRoot>
            <avalonDock:LayoutPanel Orientation="Horizontal">
                <avalonDock:LayoutDocumentPane/>
                <avalonDock:LayoutAnchorablePane Name="ToolsPane" DockWidth="240"/>
            </avalonDock:LayoutPanel>
        </avalonDock:LayoutRoot>
    </avalonDock:DockingManager>

使用自定义 class LayoutUpdateStrategy:

public class LayoutUpdateStrategy : ILayoutUpdateStrategy
{
    public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown)
    {}

    public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown)
    {}

    public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)
    {
        //AD wants to add the anchorable into destinationContainer
        //just for test provide a new anchorablepane 
        //if the pane is floating let the manager go ahead
        LayoutAnchorablePane destPane = destinationContainer as LayoutAnchorablePane;
        if (destinationContainer != null &&
            destinationContainer.FindParent<LayoutFloatingWindow>() != null)
            return false;

        var toolsPane = layout.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault(d => d.Name == "ToolsPane");
        if (toolsPane != null)
        {
            toolsPane.Children.Add(anchorableToShow);
            return true;
        }
        return false;
    }

    public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer)
    {
        return false;
    }
}

自定义布局选择器:

class AutobinderLayoutSelector : StyleSelector
{
    public Style DocumentStyle { get; set; }
    public Style ToolStyle { get; set; }

    public override Style SelectStyle(object item, DependencyObject container)
    {
        //check if the item is an instance of TestViewModel
        if (item is IDockToolBar)
            return DocumentStyle;
        else if (item is IDockDocument)
            return ToolStyle;

        //delegate the call to base class
        return base.SelectStyle(item, container);
    }
}

和自定义模板选择器:

public class AutobinderTemplateSelector : DataTemplateSelector
{
    public DataTemplate DocumentTemplate { get; set; }
    public DataTemplate ToolTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //check if the item is an instance of TestViewModel
        if (item is IDockToolBar)
            return DocumentTemplate;
        else if (item is IDockDocument)
            return ToolTemplate;

        //delegate the call to base class
        return base.SelectTemplate(item, container);
    }
}

public class AutobinderTemplate : DataTemplate
{

}

public interface IDockToolBar {
    bool IsVisible { get; set; }
}

public interface IDockDocument { }