Franz Gsell
Franz Gsell

Reputation: 1565

AvalonDock MVVM dynamic view

I am using the AvalonDock from the Xceed.Wpf.AvalonDock package (version 3.5). The docking manager is part of my main view. The interesing part is here:

    <xcad:DockingManager Name="_dockingManager" Grid.Row="1" DataContext="{Binding DockingManagerViewModel}"
                     DocumentsSource="{Binding Documents}"
                     AnchorablesSource="{Binding Anchorables}" >

        <xcad:DockingManager.Resources>

            <DataTemplate DataType="{x:Type vm:DockingWindowViewModel}">
                <v:SampleDockWindowView />
            </DataTemplate>

        </xcad:DockingManager.Resources>

        <xcad: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>
        </xcad:DockingManager.LayoutItemContainerStyle>

    </xcad:DockingManager>

So the main view model contains an observable collection (e.g. "Documents") where I can add view models dynamically. The problem is now that I have different views for the different view models and the views will be added during runtime. Currently the views are retrieved based on the data templates:

    <xcad:DockingManager.Resources>

        <DataTemplate DataType="{x:Type vm:DockingWindowViewModel}">
            <v:SampleDockWindowView />
        </DataTemplate>

    </xcad:DockingManager.Resources>

How can I change this to get a dynamic view based on the corresponding view model?

Upvotes: 0

Views: 1311

Answers (2)

trix
trix

Reputation: 898

As some people already answered here, you need to add a template selector. So an example may drive you.

Supposing you have 2 couple of view/viewmodel named

MyDocViewA -> MyDocAViewModel

MyDocViewB -> MyDocBViewModel

On the main view XAML code, within you DockingManager code, you need to add a LayoutItemTemplateSelector like this:

                <xcad:DockingManager.LayoutItemTemplateSelector>
                <s:PanesTemplateSelector>
                    <s:PanesTemplateSelector.MyDocViewATemplate>
                        <DataTemplate>
                            <p:MyDocViewA />
                        </DataTemplate>
                    </s:PanesTemplateSelector.MyDocViewATemplate>
                    <s:PanesTemplateSelector.MyDocViewBTemplate>
                        <DataTemplate>
                            <p:MyDocViewB />
                        </DataTemplate>
                    </s:PanesTemplateSelector.MyDocViewBTemplate>                        
            </xcad:DockingManager.LayoutItemTemplateSelector>

Where s is the namespace where you define the PanesTemplateSelector as following:

    class PanesTemplateSelector : DataTemplateSelector
{
    public DataTemplate MyDocViewATemplate { get; set; }
    public DataTemplate MyDocViewBTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is MyDocAViewModel)
            return MyDocViewATemplate;

        if (item is MyDocBViewModel)
            return MyDocViewBTemplate;
    }
}

Assuming that MyDocAViewModel is the ViewModel for view MyDocViewA and MyDocBViewModel is the ViewModel for view MyDocViewB.

That's it. Now you can add instances of MyDocAViewModel and MyDocBViewModel to your observable collection (eg. "Documents") of your DockingManager and get it bindinded to MyDocViewA and MyDocViewB respectively.

Upvotes: 1

Haukinger
Haukinger

Reputation: 10863

View to view model is normally a one-to-one mapping, so just add a lot of DataTemplates... either from xaml or from code or a combination of both (a xaml per module and code in the modules initializer to merge the module's ResourceDictionary into that of the app).

Upvotes: 0

Related Questions