Wpf下AvalonDock使用Prism8进行导航

AIStudio框架汇总及介绍

参考博客:AvalonDock使用Prism进行导航

本文不在介绍具体内容,上面那个博客介绍的很详细,这里只补充一下几个细节。

1.建立一个ViewModel与Avalon进行绑定交互。

 public abstract class DockWindowViewModel : BindableBase, INavigationAware
    {
        #region Properties

        #region CloseCommand
        private ICommand _CloseCommand;
        public ICommand CloseCommand
        {
            get
            {
                if (_CloseCommand == null)
                    _CloseCommand = new DelegateCommand(() => Close());
                return _CloseCommand;
            }
        }
        #endregion

        #region IsClosed
        private bool _IsClosed;
        public bool IsClosed
        {
            get { return _IsClosed; }
            set
            {
                if (_IsClosed != value)
                {
                    _IsClosed = value;
                    RaisePropertyChanged("IsClosed");
                }
            }
        }
        #endregion

        #region CanClose
        private bool _CanClose;
        public bool CanClose
        {
            get { return _CanClose; }
            set
            {
                if (_CanClose != value)
                {
                    _CanClose = value;
                    RaisePropertyChanged("CanClose");
                }
            }
        }
        #endregion

        #region CanFloat
        private bool _CanFloat;
        public bool CanFloat
        {
            get { return _CanFloat; }
            set
            {
                if (_CanFloat != value)
                {
                    _CanFloat = value;
                    RaisePropertyChanged("CanFloat");
                }
            }
        }
        #endregion

        #region Title
        private string _Title;
        public string Title
        {
            get { return _Title; }
            set
            {
                if (_Title != value)
                {
                    _Title = value;
                    RaisePropertyChanged("Title");
                }
            }
        }
        #endregion

        #region IconSource

        private ImageSource _IconSource = null;
        public ImageSource IconSource
        {
            get { return _IconSource; }
            set
            {
                if (_IconSource != value)
                {
                    _IconSource = value;
                    RaisePropertyChanged("IconSource");
                }
            }
        }

        #endregion


        #endregion

        #region IsSelected
        private bool _IsSelected;
        public bool IsSelected
        {
            get { return _IsSelected; }
            set
            {
                if (_IsSelected != value)
                {
                    _IsSelected = value;
                    RaisePropertyChanged("IsSelected");
                }
            }
        }
        #endregion

        #region MaxTabItemNumber 控制是否可以多开
        public int MaxTabItemNumber { get; set; } = 1;
        public int TabItemNumber { get; set; } = 1;
        #endregion



        public DockWindowViewModel()
        {
            this.CanClose = true;
            this.CanFloat = true;
            this.IsClosed = false;
        }

        public virtual void Close()
        {
            this.IsClosed = true;
        }


        public virtual void OnNavigatedTo(NavigationContext navigationContext)
        {
            IsSelected = true;
        }

        public virtual bool IsNavigationTarget(NavigationContext navigationContext)
        {          
            if (TabItemNumber < MaxTabItemNumber)
            {
                TabItemNumber++;
                return false;
            }
            else
            {             
                return true;
            }      
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {

        }
    }

2.对AvalonDockingRegionAdapter进行改进

var TitleBinding = new Binding("Title") { Source = item.DataContext };
BindingOperations.SetBinding(newLayoutDocument, LayoutDocument.TitleProperty, TitleBinding);

Title是依赖性属性,对依赖性属性进行绑定,ViewModel上直接设置标题Title就是TabItem的标题Title。

但是CanClose,CanFloat,IconSource不是依赖性属性,无法使用自带的方法进行绑定。引入Kent.Boogaart.Truss.BindingManager进行绑定

bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.CanClose, newLayoutDocument, d => d.CanClose) { Mode = Kent.Boogaart.Truss.BindingMode.OneWayToSource });
bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.CanFloat, newLayoutDocument, d => d.CanFloat) { Mode = Kent.Boogaart.Truss.BindingMode.OneWayToSource });
bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.IconSource, newLayoutDocument, d => d.IconSource) { Mode = Kent.Boogaart.Truss.BindingMode.OneWayToSource });
bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.IsSelected, newLayoutDocument, d => d.IsSelected) { Mode = Kent.Boogaart.Truss.BindingMode.TwoWay });

前三项不是必须项,最后那个IsSelected很重要,用来控制TabItem的激活。


AvalonDockingRegionAdapter完整代码如下

using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Threading;
using Xceed.Wpf.AvalonDock;
using Xceed.Wpf.AvalonDock.Layout;

namespace AIStudio.Wpf.AvalonDockPrism.Avalon
{
    public class AvalonDockingRegionAdapter : RegionAdapterBase<DockingManager>
    {
        #region Constructor

        public AvalonDockingRegionAdapter(IRegionBehaviorFactory factory): base(factory)
        {
            bindingManager = new Kent.Boogaart.Truss.BindingManager();
        }

        #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

        private Kent.Boogaart.Truss.BindingManager bindingManager;

        /// <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;

                        var TitleBinding = new Binding("Title") { Source = item.DataContext };
                        BindingOperations.SetBinding(newLayoutDocument, LayoutDocument.TitleProperty, TitleBinding);

                        DockWindowViewModel viewModel = (DockWindowViewModel)item.DataContext;
                        if (viewModel != null)
                        {
                            newLayoutDocument.CanClose = viewModel.CanClose;
                            newLayoutDocument.CanFloat = viewModel.CanFloat;
                            newLayoutDocument.IconSource = viewModel.IconSource;
       
                            bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.CanClose, newLayoutDocument, d => d.CanClose) { Mode = Kent.Boogaart.Truss.BindingMode.OneWayToSource });
                            bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.CanFloat, newLayoutDocument, d => d.CanFloat) { Mode = Kent.Boogaart.Truss.BindingMode.OneWayToSource });
                            bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.IconSource, newLayoutDocument, d => d.IconSource) { Mode = Kent.Boogaart.Truss.BindingMode.OneWayToSource });
                            bindingManager.Bindings.Add(new Kent.Boogaart.Truss.TypedBinding<DockWindowViewModel, LayoutDocument>(viewModel, tm => tm.IsSelected, newLayoutDocument, d => d.IsSelected) { Mode = Kent.Boogaart.Truss.BindingMode.TwoWay });
                        }     

                        //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;

                        App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
                        {
                            newLayoutDocument.IsSelected = 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   
    }
}

 

最后,老规矩,上完整源码地址:https://gitee.com/akwkevin/AIStudio.Wpf.AvalonDockPrism

有点更新,请看本文的后续2,如果要看本文源码,需要看源码历史记录了。

 

posted @ 2021-01-24 12:51  竹天笑  阅读(1124)  评论(0编辑  收藏  举报