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,如果要看本文源码,需要看源码历史记录了。

浙公网安备 33010602011771号