AvalonDock with Prism Region Adapter

后端 未结 1 1831
-上瘾入骨i
-上瘾入骨i 2021-02-09 18:35

I have seen some questions on SO but none of them seemed applicable for me. I want to be able to use the great Avalondock 2.0 with Prism 4. However, all the sample region adapte

相关标签:
1条回答
  • 2021-02-09 19:14

    Unfortunately, to the best of my knowledge, both the "LayoutDocumentPane" and the "LayoutAnchorablePane" do not allow the inclusion/creation of RegionAdapters, however the "DockingManager" does. One solution would be to create a RegionAdapter for the DockingManager which would then manage the instantiation of "LayoutDocuments" within the visual tree.

    The xaml would look as follows:

    <ad:DockingManager Background="AliceBlue" x:Name="WorkspaceRegion" prism:RegionManager.RegionName="WorkspaceRegion">
                            <ad:LayoutRoot>
                                <ad:LayoutPanel>
                                    <ad:LayoutDocumentPaneGroup>
                                        <ad:LayoutDocumentPane>
    
                                        </ad:LayoutDocumentPane>
                                    </ad:LayoutDocumentPaneGroup>
                                </ad:LayoutPanel>
                            </ad:LayoutRoot>
                        </ad:DockingManager>
    

    Note that the region is defined in the DockingManager tag and there exists a single LayoutDocumentPaneGroup under LayoutPanel. The LayoutDocumentPane under the LayoutDocumentPaneGroup will host the LayoutDocuments associated to the views to be added to the "WorkspaceRegion".

    As for the RegionAdapter itself refer to the code below which I provided with explanatory comments

    #region Constructor
    
            public AvalonDockRegionAdapter(IRegionBehaviorFactory factory)
                : base(factory)
            {
            }
    
            #endregion  //Constructor
    
    
            #region Overrides
    
            protected override IRegion CreateRegion()
            {
                return new AllActiveRegion();
            }
    
            protected override void Adapt(IRegion region, DockingManager regionTarget)
            {
                region.Views.CollectionChanged += delegate(
                    Object sender, NotifyCollectionChangedEventArgs e)
                    {
                        this.OnViewsCollectionChanged(sender, e, region, regionTarget);
                    };
    
                regionTarget.DocumentClosed += delegate(
                                Object sender, DocumentClosedEventArgs e)
                {
                    this.OnDocumentClosedEventArgs(sender, e, region);
                };
            }
    
            #endregion  //Overrides
    
    
            #region Event Handlers
    
            /// <summary>
            /// Handles the NotifyCollectionChangedEventArgs event.
            /// </summary>
            /// <param name="sender">The sender.</param>
            /// <param name="e">The event.</param>
            /// <param name="region">The region.</param>
            /// <param name="regionTarget">The region target.</param>
            void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DockingManager regionTarget)
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    foreach (FrameworkElement item in e.NewItems)
                    {
                        UIElement view = item as UIElement;
    
                        if (view != null)
                        {
                            //Create a new layout document to be included in the LayoutDocuemntPane (defined in xaml)
                            LayoutDocument newLayoutDocument = new LayoutDocument();
                            //Set the content of the LayoutDocument
                            newLayoutDocument.Content = item;
    
                            ViewModelBase_2 viewModel = (ViewModelBase_2)item.DataContext;
    
                            if (viewModel != null)
                            {
                                //All my viewmodels have properties DisplayName and IconKey
                                newLayoutDocument.Title = viewModel.DisplayName;
                                //GetImageUri is custom made method which gets the icon for the LayoutDocument
                                newLayoutDocument.IconSource = this.GetImageUri(viewModel.IconKey);
                            }
    
                            //Store all LayoutDocuments already pertaining to the LayoutDocumentPane (defined in xaml)
                            List<LayoutDocument> oldLayoutDocuments = new List<LayoutDocument>();
                            //Get the current ILayoutDocumentPane ... Depending on the arrangement of the views this can be either 
                            //a simple LayoutDocumentPane or a LayoutDocumentPaneGroup
                            ILayoutDocumentPane currentILayoutDocumentPane = (ILayoutDocumentPane)regionTarget.Layout.RootPanel.Children[0];
    
                            if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPaneGroup))
                            {
                                //If the current ILayoutDocumentPane turns out to be a group
                                //Get the children (LayoutDocuments) of the first pane
                                LayoutDocumentPane oldLayoutDocumentPane = (LayoutDocumentPane)currentILayoutDocumentPane.Children.ToList()[0];
                                foreach (LayoutDocument child in oldLayoutDocumentPane.Children)
                                {
                                    oldLayoutDocuments.Insert(0, child);
                                }
                            }
                            else if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPane))
                            {
                                //If the current ILayoutDocumentPane turns out to be a simple pane
                                //Get the children (LayoutDocuments) of the single existing pane.
                                foreach (LayoutDocument child in currentILayoutDocumentPane.Children)
                                {
                                    oldLayoutDocuments.Insert(0, child);
                                }
                            }
    
                            //Create a new LayoutDocumentPane and inserts your new LayoutDocument
                            LayoutDocumentPane newLayoutDocumentPane = new LayoutDocumentPane();
                            newLayoutDocumentPane.InsertChildAt(0, newLayoutDocument);
    
                            //Append to the new LayoutDocumentPane the old LayoutDocuments
                            foreach (LayoutDocument doc in oldLayoutDocuments)
                            {
                                newLayoutDocumentPane.InsertChildAt(0, doc);
                            }
    
                            //Traverse the visual tree of the xaml and replace the LayoutDocumentPane (or LayoutDocumentPaneGroup) in xaml
                            //with your new LayoutDocumentPane (or LayoutDocumentPaneGroup)
                            if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPane))
                                regionTarget.Layout.RootPanel.ReplaceChildAt(0, newLayoutDocumentPane);
                            else if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPaneGroup))
                            {
                                currentILayoutDocumentPane.ReplaceChild(currentILayoutDocumentPane.Children.ToList()[0], newLayoutDocumentPane);
                                regionTarget.Layout.RootPanel.ReplaceChildAt(0, currentILayoutDocumentPane);
                            }
                            newLayoutDocument.IsActive = true;
                        }
                    }
                }
            }
    
            /// <summary>
            /// Handles the DocumentClosedEventArgs event raised by the DockingNanager when
            /// one of the LayoutContent it hosts is closed.
            /// </summary>
            /// <param name="sender">The sender</param>
            /// <param name="e">The event.</param>
            /// <param name="region">The region.</param>
            void OnDocumentClosedEventArgs(object sender, DocumentClosedEventArgs e, IRegion region)
            {
                region.Remove(e.Document.Content);
            }
    
            #endregion  //Event handlers
    

    Do not forget to add the code below in your Bootstrapper so that Prism is aware of the existence of your RegionAdapter

    protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
            {
                // Call base method
                var mappings = base.ConfigureRegionAdapterMappings();
                if (mappings == null) return null;
    
                // Add custom mappings
                mappings.RegisterMapping(typeof(DockingManager),
                    ServiceLocator.Current.GetInstance<AvalonDockRegionAdapter>());
    
                // Set return value
                return mappings;
            }
    

    Voilà. I know this is not the cleanest of solutions but it should work. The same approach can easily be applied to "LayoutAnchorablePane".

    Live long and prosper!

    0 讨论(0)
提交回复
热议问题