Как привязать ObservableCollection к AvalonDock DocumentPaneGroup?

Мне нужно загрузить коллекцию элементов в виде документов в 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, назначенную текущему активному документу.

Спасибо за ваше время.


person Johnny Clara    schedule 19.11.2014    source источник


Ответы (1)


Причина, по которой вы не можете видеть какой-либо контент, заключается в том, как вы определили свои LayoutItem шаблоны. Это не сработает.
Также рассмотрите возможность использования пользовательского элемента управления вместо Frame. Frame очень тяжелый. Если вам не нужно отображать HTML, избегайте этого элемента управления. Навигацию по содержимому очень легко реализовать, если вы хотите показать содержимое, по которому можно перемещаться. Просто оберните содержимое документа в файл UserControl.

Вы правильно используете свойство ActiveContent.

Чтобы решить вашу проблему, у вас есть три рекомендуемых решения, первое из которых не совсем соответствует вашим требованиям. Поскольку вы определили DockingManager.LayoutItemTemplate неправильно, я все равно покажу это.

Решение 1. Локальный LayoutItemTemplate

Если вам нужен только один шаблон для всех контейнеров LayoutDocument и LayoutAnchorable, вы можете использовать свойство DockingManager.LayoutItemTemplate. Это свойство принимает один DataTemplate. Вложенные определения DataTemplate, как и в вашем коде, обычно не поддерживаются 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.

WPF будет автоматически применять неявный DataTemplate к каждому типу данных, который соответствует DataTemplate.TargetType этого шаблона.
DataTemplate является неявным, если ему не назначено явное значение x:Key. Чтобы гарантировать, что DataTemplate действительно может применяться автоматически, DataTemplate также должен быть определен в той же области ресурсов, что и целевой тип. Например, определение DataTemplate в Application.Resources файла App.xaml приведет к автоматическому применению шаблона в области приложения.

<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.
DataTemplateSelector — рекомендуемый выбор, если выбор DataTemplate может зависеть от более сложных ограничений, чем только тип данных модели. Это позволяет, например. оценить элемент данных и выбрать подходящий шаблон на основе определенных критериев.

Чтобы определить селектор шаблонов, вы должны расширить DataTemplateSelector, который, как ожидается, вернет DataTemplate.
Самый простой способ — определить шаблоны в словаре ресурсов App.xaml с помощью x:Key, а затем выберите из них на основе условия.
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>
person BionicCode    schedule 28.08.2020
comment
Большое спасибо за ответ. Возможно ли также иметь несколько «LayoutDocumentPane» — по одному для каждого типа данных? И как мне адресовать правильные объекты в «LayoutPane»? stackoverflow.com/questions/63630628 / - person niyou; 31.08.2020
comment
Я разместил ответ на ваш связанный вопрос. - person BionicCode; 31.08.2020