65.9K
CodeProject 正在变化。 阅读更多。
Home

可停靠的 CAB 工作区

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.89/5 (26投票s)

2006年2月15日

CPOL

5分钟阅读

viewsIcon

122101

downloadIcon

1467

使用 DivElements SandDock 控件的 CompositeUI Application Block 的自定义工作区

必备组件

本文涵盖的代码使用了 DivElements 的专有控件套件。您必须购买 DivElements 产品许可证或下载 30 天试用版才能运行代码。您可以在 http://www.divelements.com/ 下载 SandDock。

引言

复合 UI 应用程序块 (CAB) 是 Microsoft 最新推出的产品,旨在帮助开发人员创建可扩展、模块化、可插拔的应用程序。如果您不熟悉 CAB,我强烈建议您停止阅读本文,然后前往 MSDN 阅读一些可用的文档。还有一些动手实验将帮助您了解 CAB 的全部内容。

这个应用程序块提供了许多功能。我花了过去三周的时间研究它,才刚刚开始将这些部分联系起来。本文涵盖的部分是工作区。下面的图形显示了我的 DockableWindowWorkspaceTabbedDocumentWorkspace 的实际运行效果。

[Docking Hints]

[Side By Side]

工作区傻瓜指南

我不敢声称完全理解复合 UI 应用程序块背后的概念,但这是我用一分钟时间总结的工作区是什么以及如何使用它们。Workspace 作为 WorkItems 公开的 SmartPart 视图的容器。CAB 附带了几个内置的 Workspace,用于在 Windows、TabControls、Zones 和 Decks 中显示 SmartPartWindowWorkspace 在其自己的窗口中显示每个 SmartPartTabWorkspace 在其自己的 TabPage 中显示每个 SmartPart。如果您想知道 SmartPartWorkItem 是什么,那很好地说明了您在阅读本文的其余部分之前需要完成 CAB 的动手实验。

应用程序块的设计允许开发人员在不破坏应用程序的情况下更改 Workspace。这就引出了“Shell”应用程序的概念。Shell 应用程序提供一个或多个 Workspace,并允许动态加载的模块在这些 Workspace 中显示 WorkItem。可以修改 Shell 应用程序来完全改变用户与 WorkItem 交互的方式。我将通过修改其中一个动手实验样本来使用我的自定义 Workspace 来演示这个概念。

Microsoft 使创建自定义 Workspace 变得非常简单直接。其中一项动手实验将引导您使用 TreeView 控件创建自定义 Workspace。我的自定义工作区是基于 CAB 源代码下载中包含的 WindowWorkspace 创建的。

工作区设计

DockableWindowWorkspaceTabbedDocumentWorkspace 都必须使用现有的 SandDockManager 进行构造。使用过 DivElements 的 SandDock 控件的您将熟悉 SandDockManagerSandDock 控件允许您创建一个类似于 Visual Studio 提供的界面,包括可固定/可折叠的窗口,用户可以重新排列它们。请参阅下面的屏幕截图以了解此类 UI 的示例。

[Sidebar on Left]

[Sidebar Collapsed]

SandDockManager 是一个必须添加到您的 Shell 应用程序窗体的组件。下面来自 BankShellForm.cs 的代码显示了自定义 Workspace 的创建,将之前添加到窗体的 SandDockManager 传递过去。

[InjectionConstructor]
public BankShellForm(WorkItem workItem, 
                     IWorkItemTypeCatalogService workItemTypeCatalog)
  : this()
{
  this.workItem = workItem;
  this.workItemTypeCatalog = workItemTypeCatalog;

  /// ADDED
  this.sideBarWorkspace = new DockableWindowWorkspace(this.sandDockManager);
  this.contentWorkspace = new TabbedDocumentWorkspace(this.sandDockManager);
  this.workItem.Workspaces.Add(sideBarWorkspace, "sideBarWorkspace");
  this.workItem.Workspaces.Add(contentWorkspace, "contentWorkspace");
  ///
}

在内部,SandDockManager 为我们完成了大部分工作;自定义 Workspace 只是实现了 IWorkspace 接口的 SandDockManager 的包装器。

IWorkspace 暴露了显示、隐藏、激活和关闭 SmartPart 的方法。自定义 Workspace 只是显示、隐藏、激活和关闭与 SmartPart 关联的 DockableWindowTabbedDocument

持久化布局

SandDock 控件支持在应用程序实例之间持久化其布局的概念。这意味着,如果您的精明用户决定花时间重新排列他们喜欢的 Workspace 窗口和标签,SandDockManager 将创建一个 XML 文件,可以将其保存到用户的磁盘并在下次启动应用程序时加载。下面来自 BankShell.cs 的代码是一种快速而粗糙的实现,它将 SandDock 布局保存/加载到名为 SandDockLayout.txt 的文件中,该文件位于应用程序可执行文件所在的目录中。

/// ADDED
private string _ReadLayout()
{
  using (FileStream fs = System.IO.File.Open("SandDockLayout.txt",
    FileMode.OpenOrCreate, FileAccess.Read))
  {
    using (StreamReader sr = new StreamReader(fs))
    {
      return sr.ReadToEnd();
    }
  }
}

private void _WriteLayout(string layout)
{
  using( FileStream fs = System.IO.File.Open("SandDockLayout.txt",
    FileMode.Create, FileAccess.Write))
  {
    using (StreamWriter sw = new StreamWriter(fs))
    {
      sw.Write(layout);
    }
  }
}

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
  base.OnClosing(e);
  if (!e.Cancel)
  {
    _WriteLayout(this.sandDockManager.GetLayout());
  }
}
///

protected override void OnLoad(EventArgs e)
{
  //. . .

  /// ADDED
  string layout = _ReadLayout();
  if (!String.IsNullOrEmpty(layout))
  {
    try
    {
      this.sandDockManager.SetLayout(layout);
    }
    catch { }
  }
  ///
}

这是一个可以利用的绝佳功能,但对于动态加载的 WorkItem 来说可能很难。为了帮助您在应用程序中实现此功能,我创建了 IDockableSmartPart 接口,您可以使用它来标记那些需要持久化布局的 SmartPart

IDockableSmartPart

实现者只需为其 SmartPart 定义一个 GuidSandDockManager 将使用该 Guid 来识别 SmartPart 在应用程序使用之间的位置和布局。请参阅来自 SideBarView.cs 的示例代码。

public partial class SideBarView : UserControl, IDockableSmartPart
{
  private static readonly Guid dspGuid = 
    new Guid("{B69E82F9-5FA2-4309-8FC0-0F421D12F0EB}");
  
  //. . .
  
  Guid IDockableSmartPart.Guid
  {
    get { return dspGuid; }
  }
}

您可以通过运行应用程序、将 SideBarView 移动到屏幕右侧、关闭应用程序,然后重新打开应用程序来测试 BankTeller 应用程序。SideBarView 现在应该显示在屏幕的右侧。无论您将 SideBarView 放在哪里,它都应该被记住并在每次重新加载时放置在正确的位置。

[Sidebar on Left]

[Sidebar on Right]

您可以使用内置的 StateIStatePersistanceProviders 来让您的 WorkItem 在使用之间记住它们的 Guid,并将这些 Guid 传递给您的 IDockableSmartPart,以便它们能够被自定义工作区正确加载和排列。

单元测试

我从复合 UI 应用程序块的源代码中窃取了 TabWorkspaceWindowWorkspace 的单元测试,并对它们进行了修改以适应我的需求。如果您在机器上安装了 nUnit,您可以在 WCPierce.Practices.CompositeUI.WinForms.Test 程序集上运行 GUI 界面,并看到所有漂亮的绿色指示灯。我只对现有测试添加了很少的内容,所以我认为 DockableWindowWorkspaceTabbedDocumentWorkspace 的大约 90% 的功能都由单元测试覆盖。

(已更新:alexsantos 发布了修复此问题的代码。请使用上面的下载链接获取新代码)。我在 TabbedDocumentWorkspace 中遇到的一个问题是,当一个现有标签页关闭时,如何正确地将焦点设置给兄弟标签页。您中有使用 SandDock 控件经验更丰富的人可能能够解决这个问题。如果有人提出了有效的解决方案,请告诉我,我将将其合并到 Workspace 中。

结论

我想明确一点,代码下载不包含可停靠/可固定窗口框架。所有这些了不起的功能都由 DivElements 的 SandDock 控件提供。代码下载确实包含一个漂亮的小包装器,当与 Microsoft Patterns & Practices 团队的了不起工作结合使用时,它将使您能够以模块化的方式创建高度动态和可定制的应用程序。

我非常感谢评论、评分、反馈等。所以,如果您觉得这篇文章有帮助,或者您认为它很糟糕,请通过下面的评论区告诉我。

© . All rights reserved.