Wpf下AvalonDock使用Prism8进行导航
AIStudio框架汇总及介绍
本文不在介绍具体内容,上面那个博客介绍的很详细,这里只补充一下几个细节。
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,如果要看本文源码,需要看源码历史记录了。