如何将ObservableCollection绑定到AvalonDock DocumentPaneGroup? [英] How do I bind an ObservableCollection to an AvalonDock DocumentPaneGroup?
问题描述
我需要在AvalonDock 2.0中将一组项目加载为文档.这些对象继承自一个抽象类,我想根据其子类在文档内部渲染一个框架.
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.
这是我的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>
到目前为止,我已经实现了显示与OpenProjects
集合中的项目一样多的文档,但是我似乎无法在每个文档中显示任何内容.
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.
另外,我不知道我是否正确使用了ActiveContent
:我想将在当前活动文档上分配的ViewModel分配给CurrentProject
.
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.
谢谢您的时间.
推荐答案
之所以看不到任何内容,是因为您定义了LayoutItem
模板.这行不通.
还可以考虑使用自定义控件代替Frame
. Frame
非常重.除非不需要显示HTML,否则请避免使用此控件.内容导航非常容易实现,以防您想要显示可导航的内容.只需将文档内容包装到UserControl
.
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
. The Frame
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 a UserControl
.
您正在正确使用ActiveContent
属性.
要解决您的问题,您有三种推荐的解决方案,其中第一种不能完全满足您的要求.由于您定义了DockingManager.LayoutItemTemplate
错误,因此无论如何我都会显示它.
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.
如果所有LayoutDocument
和LayoutAnchorable
容器仅需要一个模板,则可以使用DockingManager.LayoutItemTemplate
属性.此属性接受单个DataTemplate
.嵌套的DataTemplate
定义(如您的代码中一样)通常不受WPF支持.
In case you only need a single template for all LayoutDocument
and LayoutAnchorable
containers, you can use the DockingManager.LayoutItemTemplate
property. This property accepts a single DataTemplate
. Nested DataTemplate
definitions, like in your code, are generally not supported by WPF.
<ad:DockingManager>
<ad:DockingManager.LayoutItemTemplate>
<DataTemplate>
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
</ad:DockingManager.LayoutItemTemplate>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane />
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
解决方案2:隐式DataTemplate
在更高级的方案中,您将基于不同的模型显示不同的视图.如果显示的内容仅取决于模型的数据类型(如您的情况),则建议的方法是提供隐式DataTemplate
定义.
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将自动将隐式DataTemplate
应用于与该模板的DataTemplate.TargetType
匹配的每种数据类型.
如果未分配x:Key
值,则DataTemplate
是隐式的.为了确保实际上可以自动应用DataTemplate
,还必须在与目标类型相同的资源范围内定义DataTemplate
.例如,在 App.xaml 的Application.Resources
中定义DataTemplate
,将使模板自动应用于应用范围.
WPF will automatically apply an implicit DataTemplate
to every data type that matches the DataTemplate.TargetType
of this template.
The DataTemplate
is implicit, if it has no explicit x:Key
value assigned. To ensure that the DataTemplate
can actually be applied automatically, the DataTemplate
must also be defined in the same resource scope as the target type. E.g., defining the DataTemplate
in Application.Resources
of App.xaml, would make the template to be applied automatically in the application scope.
<ad:DockingManager>
<ad:DockingManager.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>
</ad:DockingManager.Resources>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
解决方案3:DataTemplateSelector
使用隐式DataTemplate
定义的先前解决方案可以替换为DataTemplateSelector
. DataTemplateSelector
是另一个WPF概念,用于选择性地应用DataTemplate
.
如果选择DataTemplate
可能比单独模型的数据类型更复杂的约束,建议使用DataTemplateSelector
.它允许例如对数据项进行评估,并根据特定条件选择合适的模板.
Solution 3: DataTemplateSelector
The previous solution, which uses implicit DataTemplate
definitions, can be replaced with a DataTemplateSelector
. DataTemplateSelector
is another WPF concept to apply a DataTemplate
selectively.
A DataTemplateSelector
is the recommended choice, if selecting a DataTemplate
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.
要定义模板选择器,必须扩展DataTemplateSelector
,它应返回DataTemplate
.
最简单的方法是使用x:Key
在 App.xaml 资源字典中定义模板,然后根据条件从模板中进行选择.
DockingManger
通过分配DockingManager.LayoutItemTemplateSelector
属性来接受模板选择器:
To define a template selector, you have to extend DataTemplateSelector
, which is expected to return a DataTemplate
.
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 the DockingManager.LayoutItemTemplateSelector
property:
App.xaml
使用x:Key
定义显式的DataTemplate
:
App.xaml
Define the explicit DataTemplate
using x:Key
:
<Application.Resources>
<DataTemplate x:Key="SubclassAViewModelTemplate" DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate x:Key="SubclassBViewModelTemplate" DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate x:Key="SubclassCViewModelTemplate" DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</Application.Resources>
DocumentManagerTemplateSelector.cs
以下代码使用Switch表达式,该表达式自C#8.0起可用.可以用switch语句或级联的if语句代替它.
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.
class DocumentManagerTemplateSelector : DataTemplateSelector
{
#region Overrides of DataTemplateSelector
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return item switch
{
SubclassAViewModel _ => Application.Current.Resources["SubclassAViewModelTemplate"] as DataTemplate,
SubclassBViewModel _ => Application.Current.Resources["SubclassBViewModelTemplate"] as DataTemplate,
SubclassCViewModel _ => Application.Current.Resources["SubclassCViewModelTemplate"] as DataTemplate,
_ => base.SelectTemplate(item, container)
};
}
#endregion
}
MainWindow.xaml
<ad:DockingManager>
<xcad:DockingManager.LayoutItemTemplateSelector>
<local:DocumentManagerTemplateSelector />
</xcad:DockingManager.LayoutItemTemplateSelector>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
这篇关于如何将ObservableCollection绑定到AvalonDock DocumentPaneGroup?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!