如何将ObservableCollection绑定到AvalonDock DocumentPaneGroup?

时间:2014-11-19 12:15:29

标签: c# wpf mvvm binding avalondock

我需要在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:我想将CurrentProject分配给当前活动文档中指定的ViewModel。

感谢您的时间。

1 个答案:

答案 0 :(得分: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匹配的每种数据类型。
如果DataTemplate没有分配明确的x:Key值,则是隐式的。为了确保实际上可以自动应用DataTemplate,还必须在与目标类型相同的资源范围内定义DataTemplate。例如,在 App.xaml DataTemplate中定义Application.Resources,将使模板自动应用于应用范围。

<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定义的先前解决方案可以替换为DataTemplateSelectorDataTemplateSelector是另一个WPF概念,可以有选择地应用DataTemplate
如果选择DataTemplateSelector可能比单独使用模型的数据类型更复杂,则建议使用DataTemplate。它允许例如评估数据项并根据某些条件选择合适的模板。

要定义模板选择器,必须扩展DataTemplateSelector,该扩展器应返回DataTemplate
最简单的方法是使用x:Key App.xaml 资源字典中定义模板,然后根据条件从模板中进行选择。
DockingManger通过分配DockingManager.LayoutItemTemplateSelector属性来接受模板选择器:

App.xaml
使用DataTemplate定义显式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语句代替它。

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>