Andreas_Lng
Andreas_Lng

Reputation: 1197

Dynamically add DockablePanes

I have a CustomControl and a list of view models for that control. Using ItemsControl I'm able to create a control for each view model dynamically. Now I use AvalonDock and I want to add one DockableContent for each generated UserControl. How can this be done dynamically?

Upvotes: 1

Views: 1541

Answers (1)

Joe
Joe

Reputation: 7004

You want to bind your collection of ViewModels to the DocumentSource of the docking manager, and set up a template to bind to properties like title etc like this:

<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>

and add things dynamically using something like this:

Documents.Add(MyNewlyCreatedViewModel)

Edit: Things get more complex when you use static panes. You have to use a template selector to choose the correct template for your normal/static panes. The MVVM AvalonDock sample is pretty good. Here's how I've implemented it (Tools are static):

    <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>

with the custom 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;
    }
}

Custom Layout Selector:

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);
    }
}

and custom TemplateSelector:

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 { }

Upvotes: 3

Related Questions