AvalonDock Multiple LayoutDocumentPane com ObservableCollection diferente contendo UserControls dinâmicos criados

Aug 28 2020

Eu tenho uma IU baseada em AvalonDock com pelo menos três LayoutDocumentPanes estáticos. Como posso preencher dinamicamente esses painéis com diferentes coleções de UserControl? Eu descobri que posso definir um DocumentSourcepara todo o DockingManager e essa coleção, em seguida, pode ser usado para gerar elementos para todos os painéis.

Posso usar várias coleções de origem ou posso filtrar minha coleção para cada LayoutDocumentPane?

<xcad:DockingManager  
            DocumentsSource="{Binding MyUserControls1}">
          
            <xcad:DockingManager.Resources>
                <DataTemplate DataType="{x:Type local:MyUserControl}">
                        <local:MyUserControl/>   
                </DataTemplate>
            </xcad:DockingManager.Resources>

    <xcad:LayoutRoot>
        <xcad:LayoutPanel Orientation="Horizontal" >
            <xcad:LayoutPanel Orientation="Vertical" DockWidth="6*">
                <xcad:LayoutPanel Orientation="Horizontal">
                            <xcad:LayoutDocumentPaneGroup>
                                <xcad:LayoutDocumentPane>
                                <!-- UserControls from ObservableCollection 1-->
                                </xcad:LayoutDocumentPane>
                            </xcad:LayoutDocumentPaneGroup >
                            <xcad:LayoutDocumentPaneGroup>
                                <xcad:LayoutDocumentPane >
                                <!-- UserControls from ObservableCollection 2-->
                                </xcad:LayoutDocumentPane>
                            </xcad:LayoutDocumentPaneGroup >
                            <xcad:LayoutDocumentPaneGroup>
                                <xcad:LayoutDocumentPane >
                                <!-- UserControls from ObservableCollection 3-->
                                </xcad:LayoutDocumentPane>
                            </xcad:LayoutDocumentPaneGroup >
                </xcad:LayoutPanel>
            </xcad:LayoutPanel>
        </xcad:LayoutPanel>
     </xcad:LayoutRoot>

Respostas

1 BionicCode Aug 31 2020 at 11:52

Para controlar o layout dinamicamente, você deve implementá ILayoutUpdateStrategy-lo e atribuí-lo a DockingManager.LayoutUpdateStrategy. Esta estratégia é invocada sempre que você adiciona um item a DockingManager.DocumentsSourceou DockingManager.AnchorablesSource. Você pode lidar com os detalhes do layout antes ou depois da adição do conteúdo.

Observe que um indivíduo DataTemplatepara cada modelo de documento ainda é necessário (ver exemplos ). ILayoutUpdateStrategylida apenas com o layout, mas não com a renderização de conteúdo.

Você deve adicionar todos os documentos de tipos diferentes a uma coleção de origem comum.
Recomendo a introdução de uma interface, por exemplo, IDocumentque deve ser implementada por cada tipo de documento. Isso permite armazená-los em uma coleção comum do tipo IDocumente ativa o polimorfismo ao iterar a coleção.
Você pode então vincular ObservableCollection<IDocument>a ao DockingManager.DocumentsSource.

O exemplo a seguir também cria o layout necessário (devido à restrição de agrupamento). Como ele adicionará elementos de layout mising ao LayoutPanelde DockingManager.Layout, o layout é tratado antes de o conteúdo ser adicionado. Portanto, no caso de documentos agrupados, não há necessidade de predefinir LayoutRoot:

LayoutUpdateStrategy.cs

public class LayoutUpdateStrategy : ILayoutUpdateStrategy
{
  #region Implementation of ILayoutUpdateStrategy

  public bool BeforeInsertAnchorable(
    LayoutRoot layout,
    LayoutAnchorable anchorableToShow,
    ILayoutContainer destinationContainer)
  {
    return false;
  }

  public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown)
  {
  }

  // Creates a new LayoutDocumentPane for each document model type.
  // All grouped documents will reside in a common LayoutDocumentPaneGroup.
  public bool BeforeInsertDocument(
    LayoutRoot layout,
    LayoutDocument documentToShow,
    ILayoutContainer destinationContainer)
  {
    if (destinationContainer?.FindParent<LayoutFloatingWindow>() != null)
    {
      // Return 'false' as the strategy hasn't performed any action
      return false;
    }

    LayoutDocumentPane existingDocumentsPane = layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault(
      pane => pane.Children.Any(
        layoutDocument => layoutDocument.Content.GetType() == documentToShow.Content.GetType()));

    if (existingDocumentsPane != null)
    {
      existingDocumentsPane.Children.Add(documentToShow);
    }
    else
    {
      // Get the existing LayoutDocumentPaneGroup
      LayoutDocumentPaneGroup paneGroup = layout.Descendents().OfType<LayoutDocumentPaneGroup>().FirstOrDefault();

      // Create a new LayoutDocumentPaneGroup if there is none in the current layout
      if (paneGroup == null)
      {
        paneGroup = new LayoutDocumentPaneGroup();
        layout.RootPanel.Children.Add(paneGroup);
      }

      // Create a new LayoutDocumentPane for the new document type
      var pane = new LayoutDocumentPane(documentToShow);
      paneGroup.Children.Add(pane);
    }

    // Return 'true' as the strategy has performed any action
    return true;
  }

  public void AfterInsertDocument(LayoutRoot layout, LayoutDocument documentToShow)
  {
  }

  #endregion
}

MainWindow.xaml

<Window>
  <DockingManager DocumentsSource="{Binding MyDocuments}">
    <DockingManager.LayoutUpdateStrategy>
        <LayoutUpdateStrategy />
      </DockingManager.LayoutUpdateStrategy>
  </DockingManager>
</Window>