I need to load a collection of items as documents in AvalonDock 2.0. These objects inherit from an abstract class, for which I want to render a frame inside the document depending on which subclass are.
This is my XAML:
<ad:DockingManager Background="Gray" DocumentsSource="{Binding Path=OpenProjects}"
ActiveContent="{Binding Path=CurrentProject, Mode=TwoWay}">
<ad:DockingManager.DocumentHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=OpenProjects/Name}" />
</DataTemplate>
</ad:DockingManager.DocumentHeaderTemplate>
<ad:DockingManager.LayoutItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</Grid.Resources>
</Grid>
</DataTemplate>
</ad:DockingManager.LayoutItemTemplate>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
So far I've achieved to show as many documents as items are in the OpenProjects
collection, but I can't seem to show anything inside each document.
Plus, I don't know if I'm using ActiveContent
properly: I want to assign to CurrentProject
the ViewModel assigned on the current active document.
Thank you for your time.
The reason why you are not able to see any content is of the way you defined your
LayoutItem
templates. This can't work.Also consider to use a custom control instead of the
Frame
. TheFrame
is very heavy. Unless you don't need to display HTML, avoid this control. Content navigation is very easy to implement, in case you want to show navigable content. Just wrap your document content into aUserControl
.You are using the
ActiveContent
property properly.To fix your problem you have three recommended solutions, where the first doesn't exactly meet your requirements. Since you defined the
DockingManager.LayoutItemTemplate
wrong, I will show it anyway.Solution 1: Local
LayoutItemTemplate
In case you only need a single template for all
LayoutDocument
andLayoutAnchorable
containers, you can use theDockingManager.LayoutItemTemplate
property. This property accepts a singleDataTemplate
. NestedDataTemplate
definitions, like in your code, are generally not supported by WPF.Solution 2: Implicit
DataTemplate
In more advanced scenarios you display different views based on different models. If the displayed content depends on the data type of the model alone (like in your case), the recommended approach is to provide implicit
DataTemplate
definitions.WPF will automatically apply an implicit
DataTemplate
to every data type that matches theDataTemplate.TargetType
of this template.The
DataTemplate
is implicit, if it has no explicitx:Key
value assigned. To ensure that theDataTemplate
can actually be applied automatically, theDataTemplate
must also be defined in the same resource scope as the target type. E.g., defining theDataTemplate
inApplication.Resources
of App.xaml, would make the template to be applied automatically in the application scope.Solution 3:
DataTemplateSelector
The previous solution, which uses implicit
DataTemplate
definitions, can be replaced with aDataTemplateSelector
.DataTemplateSelector
is another WPF concept to apply aDataTemplate
selectively.A
DataTemplateSelector
is the recommended choice, if selecting aDataTemplate
may depend on more complex constraints, than the model's data type alone. It allows to e.g. evaluate the data item and chose the appropriate template based on certain criteria.To define a template selector, you have to extend
DataTemplateSelector
, which is expected to return aDataTemplate
.The easiest way is to define the templates in App.xaml resource dictionary using an
x:Key
and then select from them based on the condition.DockingManger
accepts a template selector by assigning theDockingManager.LayoutItemTemplateSelector
property:App.xaml
Define the explicit
DataTemplate
usingx:Key
:DocumentManagerTemplateSelector.cs
The following code uses the Switch Expression, which is available since C# 8.0. It can be replaced with a switch statement or cascaded if-statements.
MainWindow.xaml