AvalonDockではPrismでナビゲート

11348 ワード

最近会社のプロジェクトでAvalonDockのフォームをPrismでナビゲートする必要があるので検討してみました.正直Avalonは使いづらい感じで、「Avalonの使いにくくて、我が王の理想の郷を汚した」というのは私が初めてAvalonを使った後の考えです.しかし、WPFの下でMVVMをサポートしているDockも他に発見されず、少なくともみんなこれを使っています.仕方ない~
今回の経験はStakOverFlowとPrismの公式紹介から学びました.英語の上手な友达は直接原文を読むことができます.
質問の紹介
実現するのはVSのような効果です.以下の機能があります.
  • Dock機能、サブスクリーン
  • をドラッグできる
  • ツリー構造からノードをクリックし、メインウィンドウにノード内容
  • を表示する.
    解決策
    に質問
    ソリューション
    1.Dock機能で、画面をドラッグできる
    AvalonDock
    2.ツリー構造からノードをクリックし、メインウィンドウにノードの内容を表示する
    PrismのNavigation機能
    PS:AvalonDockとPrismの基礎の友达がいなければ、まず基礎知識を補充します.
    AvalonDockのメインベアラインタフェース(つまりVSのコード編集部)は、TabControlに非常に似ていることがわかります.ではPrismはどのようにしてTabControlのナビゲーションを行うのでしょうか.
  • Moduleでナビゲーションが必要と宣言されたView
  • ModuleのModuleクラスにView
  • が登録されています.
    public class ModuleTestModule : IModule
    {
          IRegionManager _regionManager;
          IUnityContainer _container;
    
          public ModuleTestModule(RegionManager regionManager, IUnityContainer container)
          {
                _regionManager = regionManager;
                _container = container;
          }
    
          public void Initalize()
          {
                //TestView         View   
                //    View   
                _container.RegisterTypeForNavigation();
          }
    }
    
  • Shellで定義Region
  • 
    
    
  • BootstrapperにModule
  • を追加
    protected override void ConfigureModuleCatalog()
    {
          var moduleCatalog = (ModuleCatalog)ModuleCatalog;
          moduleCatalog.AddModule(typeof(SolutionExplorer.SolutionExplorerModule));
    }
    
  • 適宜ナビゲーション
  • を行う.
    //          Region,          View
    regionManager.RequestNavigation("TestRegion","TestView");
    

    以上が基本的なTabControlへのナビゲーションです.
    猫に虎を描いて、Avalonでこのように使ってみましょう.次はAvalonDockがプライマリベアラ部分を宣言するコードです.
            
                
                    
                        
                            
    
                            
                        
                    
                
            
    

    StakeOverFlowによると、LayoutPanelやLayoutDocumentPaneGroupなどはprism:RegionManager.RegionName=「xxx」を使用できないという.しかし、この話をしたのは数年前で、環境はAvalonDock 2とPrism 4だった.現在のPrismは6になっていますが、支持するかどうかは分かりませんが、試したことがありません.興味のある方は試してみてください.
    だからAvalonにRegionを加えて
            
                
                    
                        
                            
    
                            
                        
                    
                
            
    

    TabControlの追加と変わらないように見えます.そして運転してみましょう.うん、思いがけず新聞を間違えた.//あとで図をとる
    RegionAdapterが少なくなったことを示しています.うん?何だ,見たことがない.文字通りRegionのアダプタです.何してるの?Regionをナビゲートするとき、どのような動作を実行すべきか、カプセル化します.Regionとターゲットタイプを1つにします.以下はPrism公式紹介の原話です.
    Region adapters control how items placed in a region interact with the host control.アダプタは、キャリアコントロールと対話することで、Region内の特定のアイテムの位置を制御します.
    はい、アダプタが少なくなったら、建てましょう.経験のある友達はもうクラスを書き始めたかもしれません.
    public class AvalonDockingRegionAdapter : IRegionAdapter
    {  
        //IRegionAdapter   
        public IRegion Initialize(object regionTarget, string regionName)
        {
            ...
        }
    }
    

    しかし、これは正しい開き方ではありません.前回の公式原話:
    To create a region adapter, you derive your class from RegionAdapterBase and implement the CreateRegion and Adapt methods. Optionally, override the AttachBehaviors method to attach special logic to customize the region behavior. If you want to interact with the control that hosts the region,Region Adapterを作成するには、RegionAdapterBaseを継承し、CreateReionとAdaptメソッドを実装する必要があります.必要に応じて、AttachBehaviorsメソッドを書き換えて、カスタムRegion Behaviorに特別なロジックを追加します.Regionを搭載したコントロールと対話したい場合は、IHostAwareRegionBehaviorを実装する必要があります.
    灰はよくいいですね.私たちはそれに従います.
        public class AvalonDockingRegionAdapter : RegionAdapterBase
        {
           #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
    
            /// 
            /// Handles the NotifyCollectionChangedEventArgs event.
            /// 
            /// The sender.
            /// The event.
            /// The region.
            /// The region target.
            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 oldLayoutDocuments = new List();
                            //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;
                        }
                    }
                }
            }
    
            /// 
            /// Handles the DocumentClosedEventArgs event raised by the DockingNanager when
            /// one of the LayoutContent it hosts is closed.
            /// 
            /// The sender
            /// The event.
            /// The region.
            void OnDocumentClosedEventArgs(object sender, DocumentClosedEventArgs e, IRegion region)
            {
                region.Remove(e.Document.Content);
            }
    
            #endregion   
        }
    

    Bootstrapperに追加してください
    // Bootstrapper.cs
    protected virtual RegionAdapterMappings ConfigureRegionAdapterMappings()
    {
        RegionAdapterMappings regionAdapterMappings = ServiceLocator.Current.GetInstance();
        if (regionAdapterMappings != null)
        {
            regionAdapterMappings.RegisterMapping(typeof(DockingManager), ServiceLocator.Current.GetInstance());
        }
        return regionAdapterMappings;
    }
    

    はい、終わります.仕事を終わります.