Dragablz的MVVM绑定初探

Dragablz的MVVM绑定初探

Dragablz(https://github.com/ButchersBoy/Dragablz)是一套使用了Google Modern Design的多窗口选项卡管理框架,可以用在WPF和UWP应用上。

此前,我一般使用AvalonDock做为多文档式应用(MDI)的替代品。Avalondock 功能强大,但是其界面比较传统,与现代设计风格有较大差异。前几天开始使用Dragablz时,发现其应用简单,使用方便,在窗口管理不需要特别复杂的情况下使用Dragablz更加美观。

Dragablz支持内嵌“窗口”,或者称为标签页,和浮动“窗口”,称为工具窗口。下文中只涉及到标签页。

首先看一下主窗口的视图模型(MainWindowViewModel.cs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class MainWindowViewModel:ViewModelBase
{
static MainWindowViewModel _workspace = new MainWindowViewModel();
static MainWindowViewModel _tabHost = new MainWindowViewModel();
//系统的默认VM
public static MainWindowViewModel WorkSpace
{ get { return _workspace; } }
//新窗口的VM
public static MainWindowViewModel TabHost
{ get { return _tabHost; } }
//标签页文档
public ObservableCollection<SoconaViewModel> Documents
{ get; private set; }
//窗口管理器
private readonly IInterTabClient _interTabClient = new SoconaInterTabClient();
public IInterTabClient InterTabClient
{ get { return _interTabClient; } }
//浮动窗口
public ObservableCollection<object> Anchorables { get; private set; }
//活动文档
private SoconaViewModel activeTab;
public SoconaViewModel ActiveTab
{
get { return activePane; }
set
{
activePane = value;
OnPropertyChanged();
}
}
//关闭窗口事件回调
public ItemActionCallback ClosingTabItemHandler
{
get { return ClosingTabItemHandlerImpl; }
}
private void ClosingTabItemHandlerImpl(
ItemActionCallbackArgs<TabablzControl> args )
{
//in here you can dispose stuff or cancel the close
//here's your view model:
var viewModel = args.DragablzItem.DataContext as SoconaViewModel;
Debug.Assert(viewModel != null);
//here's how you can cancel stuff:
//args.Cancel();
if (viewModel.IsDirty)
{
var res = MessageBox.Show(string.Format("Save changes for file '{0}'?", viewModel.Title), "Socona App", MessageBoxButton.YesNoCancel);
if (res == MessageBoxResult.Cancel)
{ return; }
if (res == MessageBoxResult.Yes)
{
if (viewModel.UpdateCommand.CanExecute(null))
{
viewModel.UpdateCommand.Execute(null);
}
}
}
CloseTab(viewModel);
}
}

MainWindow的绑定代码如下(MainWindow.xaml)

1
2
3
4
5
6
7
<!-- a root, named tab control is needed, so when a tab is torn and a new window is created, the new target tab control can be identified -->
<dragablz:TabablzControl x:Name="InitialTabablzControl" SelectedItem="{Binding ActivePane}"
AddLocationHint="After" Style="{StaticResource TabablzControlStyle}" >
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController InterTabClient="{Binding InterTabClient}" Partition="2AE89D18-F236-4D20-9605-6C03319038E6" />
</dragablz:TabablzControl.InterTabController>
</dragablz:TabablzControl>

在Window.Resources中加入对应的样式TabablzControlStyle,注意,这在里指定的视图的数据源ItemSource,和关闭窗口回调。最后指定的窗口控制器是不需要改写的,使用库中的类即可。

1
2
3
4
5
6
7
<Style TargetType="{x:Type dragablz:TabablzControl}" x:Key="TabablzControlStyle" BasedOn="{StaticResource MaterialDesignTabablzControlStyle}">
<Setter Property="ItemsSource" Value="{Binding Documents}" />
<Setter Property="ClosingItemCallback" Value="{Binding ClosingTabItemHandler}" />
<Setter Property="HeaderMemberPath" Value="Title" />
<Setter Property="InterTabController" Value="{StaticResource InterTabController}" />
</Style.Triggers>
</Style>

同样在Window.Resources中加入VM中Document的数据模板,这样在向VM.Documents中增加页面视图模型时,就使用匹配的数据模块显示指定的视图。

1
2
3
<DataTemplate DataType="{x:Type vm:SoconaViewModel}">
<v:SoconaView></v:SoconaView>
</DataTemplate>

最后需要实现的是窗口管理器类,这个管理器类用于将一个页面拖动离开当前主窗口,要产生一个新的主窗口,这个类用来管理产生新的主窗口的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
internal class SoconaInterTabClient : IInterTabClient
{
//控制产生新窗口时的逻辑。
public INewTabHost<Window> GetNewHost(IInterTabClient interTabClient, object partition, TabablzControl source)
{
var view = new MainWindow();
var model = MainWindowViewModel.TabHost;
view.DataContext = model;
return new NewTabHost<Window>(view, view.InitialTabablzControl);
}
//控制关闭窗口时的逻辑
public TabEmptiedResponse TabEmptiedHandler(TabablzControl tabControl, Window window)
{
//当非主窗口中关闭最后一个标签的同时关闭这个窗口
//主窗口可以存在0个标签页
if (window.DataContext == MainWindowViewModel.WorkSpace)
{
return TabEmptiedResponse.DoNothing;
}
else
{
return TabEmptiedResponse.CloseWindowOrLayoutBranch;
}
}
}

这个框架非常好用,和MVVM模型的集成也非常好。感兴趣的可以自己去试用一下。