如何将ObservableCollection绑定到AvalonDock的DocumentPaneGroup?

5

我需要在AvalonDock 2.0中将一组项目作为文档加载。这些对象继承自一个抽象类,我希望根据其子类来在文档内呈现框架。

以下是我的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集合中的条目数量相同的文档,但似乎无法显示每个文档内部的任何内容。

此外,我不知道我是否正确使用了ActiveContent:我想将当前活动文档上分配的ViewModel分配给CurrentProject

感谢您的时间。

1个回答

4
你看不到任何内容的原因是你定义了 LayoutItem 模板的方式不正确,这样是不能正常工作的。此外,考虑使用自定义控件代替 FrameFrame 很重,除非你不需要显示 HTML,否则应避免使用该控件。如果你想显示可导航的内容,内容导航很容易实现,只需将文档内容包装在 UserControl 中。
你已经正确地使用了 ActiveContent 属性。
为了解决你的问题,有三个建议的解决方案,第一个解决方案并不完全满足你的要求。由于你错误地定义了 DockingManager.LayoutItemTemplate,我还是会展示第一种解决方案。
解决方案1:本地 LayoutItemTemplate 如果你只需要为所有 LayoutDocumentLayoutAnchorable 容器使用单个模板,可以使用 DockingManager.LayoutItemTemplate 属性。该属性接受单个 DataTemplate。通常不支持 WPF 中的嵌套 DataTemplate 定义,就像你的代码一样。
<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 定义的方法。

WPF 将自动将隐式 DataTemplate 应用于与此模板的 DataTemplate.TargetType 匹配的每种数据类型。
如果没有明确指定 x:Key 值,则 DataTemplate 是隐式的。为了确保可以自动应用 DataTemplate,它还必须在与目标类型相同的资源范围内定义。例如,在 App.xamlApplication.Resources 中定义 DataTemplate,将使该模板在应用程序范围内自动应用。

<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。例如,它允许评估数据项并根据某些条件选择适当的模板。

要定义模板选择器,您必须扩展DataTemplateSelector,它预计会返回DataTemplate
最简单的方法是在App.xaml资源字典中使用x:Key定义模板,然后根据条件从中选择。
通过赋值DockingManager.LayoutItemTemplateSelector属性,DockingManger接受模板选择器:

App.xaml
使用x:Key定义显式DataTemplate:

<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
以下代码使用自C# 8.0推出的Switch表达式。可以替换为switch语句或级联if语句。

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>

非常感谢您的回答。我是否可以为每个DataType拥有多个'LayoutDocumentPane'?如何将正确的对象连接到'LayoutPane'上? - niyou
1
我已经在你链接的问题中发布了答案。 - BionicCode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接