如何嵌入一个应用程序到停靠库
逐步将一个应用程序转换为停靠应用程序组件
 
 
摘要
AvalonDock 是最流行的 WPF 开源停靠库。 Sofa 对其进行了封装,增加了功能,并允许 AvalonDock 轻松集成到 Prism 等多种不同架构中。
为了从停靠系统和多实例功能中获益,必须将现有应用程序转换为可托管在停靠容器中的组件。
使用 Sofa 可以快速轻松地完成此操作:本文档将逐步介绍此转换过程。
本文术语表
Sofa.Container.SofaContainer 和 Sofa.Commons.SofaComponents 是 Sofa 的主要类。
SofaContainer 类被插入到 WPF 窗口或 UserControl 中。管理此窗口或 UserControl 的应用程序在此文档中称为容器。
SofaComponent 类嵌入了用户的应用程序。此应用程序在此文档中称为组件。
示例:在以下示例中,我们将重用 CSWPFMasterDetailBinding 应用程序。我们将其转换为库,并使其成为组件。它将在 SofaBasiContainer 应用程序中运行,该应用程序是一个容器。
1. 从应用程序到组件
 
 
该应用程序由 2 个项目组成
第 1 个项目:容器: SofaBasicContainer
容器基本上只需要“包含”功能。我们使用的 SofaBasiContainer 容器在许多示例中都可以找到,并且被重用,只需稍作修改:只需调整 MEF 导入声明即可。
- BaseWindow.xaml.cs 中的 MEF [ImportMany]键设置为Sofa.Examples. SofaComponization.SofaBasicContainer_ViewContract。
第 2 个项目:组件: CSWPFMasterDetailBinding
在此步骤中,应用程序被转换为组件,并准备在之前的 SofaBasicContainer 容器中注册。
该应用程序是 CSWPFMasterDetailBinding。它来自 All-In-One Code Framework (WPF) 项目(http://1code.codeplex.com)。
- 将应用程序转换为库- 项目属性- 输出类型从 Windows 应用程序更改为类库。
- SofaBasicContainer为了使用 MEF,必须能够访问此项目的库:必须将生成输出路径设置为..\SofaBasicContainer\bin\Debug\AddIns\
- 目标框架设置为 .NET 4
 
 - 删除 App.xaml 和 App.xaml.cs。在某些情况下,现有代码必须移至容器的组件的 Usercontrol,但此处没有。
- MainWindow.xaml:<Window x : Class ="CSWPFMasterDetailBinding.MainWindow"已更改为<UserControl x : Class...在xaml和xaml.cs文件中,必须对某些标签(资源、触发器)、属性(Title、事件...)和部分类定义进行相应的调整。
 
- 项目属性
- 为了成为组件,库必须能够注册到容器。这是使用 MEF 完成的。在 MainWindow.xaml.cs 中添加了 [Export]声明和元数据。- 必须添加对 System.ComponentModel.Composition.CodeplexDLL 的引用才能使用 MEF。还需要引用Sofa库。
- MEF [Export]声明始终相同,可以从任何示例中复制。在此步骤中,唯一重要的要编辑的数据是- 需要调整 [Export]键以匹配容器中[ImportMany]声明中使用的“Sofa.Examples. SofaComponization.SofaBasicContainer_ViewContract”键。
- ProductName(即- SofaComponent id)设置为“- CSWPFMasterDetailBinding”,并设置- Label。
 
- 需要调整 
 
- 必须添加对 
就是这样。您可以运行 SofaBasicContainer 容器,并在其菜单中找到 CSWPFMasterDetailBinding 组件。加载一个或多个,停靠,取消停靠,调整窗口大小……
当前结果:原始应用程序现在在容器中运行,并且是多实例应用程序。
但其 GUI 没有改变……
2. 从简单组件到子组件
 
 
此步骤的目标是将之前的组件拆分为 2 个组件,每个组件包含之前组件中的一个列表。
这两个组件可以由 SofaBasicContainer 托管,但它们总是协同工作,因此最好将它们托管在一个新容器中,而这个容器本身也是一个组件,托管在 SofaBasicContainer 容器中。
“1. 原始应用程序到 SofaComponent”文件夹被复制并重命名为“2. 简单 SofaComponent 到子 SofaComponents”。
第 1 个项目:容器: SofaBasicContainer
SofaBasicContainer 项目是一个标准的容器,不需要任何修改。
第 2 个项目:组件和容器:CSWPFMasterDetailBinding
- 原始组件被复制。- MainWindow.xaml 类被复制。文件和类被重命名:一个重命名为“CustomerListComponent”,另一个重命名为“OrderListComponent”。
- 在 CustomerListComponent中仅保留Customer列表(listViewCustomers),在OrderListComponent中保留Order列表(listViewOrders)。<UserControl.Resources>标签与CustomerList相关,并从OrderList中删除。
- 组件的 MEF 导出目标已更改为下一步将创建的容器。- Sofa.Examples.SofaComponization.- SofaBasicContainer_ViewContract已重命名为- Sofa.Examples.SofaComponization.- MainComponent_ViewContract
- ProductName已从- CSWPFMasterD重命名为- etailBinding- CustomerList和- OrderList,并编辑了- Labels。
 
 
- MainWindow.xaml 类被复制。文件和类被重命名:一个重命名为“
- 新的组件-容器由于我们希望这两个组件在一个窗口中打开,因此我们需要一个新容器。这个容器也将是一个组件,托管在初始 SofaBasicContainer中,取代之前的MainWindow组件。这个容器-组件是一个名为 MainComponent的新类。它是SofaBasicContainer(用于容器代码)和之前的MainWindow类(用于组件代码)的混合体。组件 (Component) - 组件的唯一代码是 MEF [Export]声明。键设置为与SofaBasicContainer的[ImportMany]声明相同的值。
 容器 - 当前项目托管一个容器:必须添加对 Sofa库的引用。
- 该类实现 IBaseContainer接口,并且必须具有public SofaContainer SofaContainer(已使用)和public SofaMenu SofaMenu(未使用)声明。
- 该类将导入之前的两个“CustomerListComponent”和“OrderListComponent”组件。其[ImportMany]声明设置为与组件的[Export]键相同的值。它实现IPartImportsSatisfiedNotification接口,以便触发OnImportsSatisfied方法。SofaBasicContainer的Compose方法必须运行一次,并且在此子容器中不存在。
- 此组件-容器的“子组件”在容器完全初始化之前无法打开。唯一有效的时间是在 SofaCommonEventHandler方法中处理“PostOpen”SofaCommonEvent时。使用了sofaContainer.OpenComponent("CustomerList")和sofaContainer.OpenComponent("OrderList")方法。组件名称是硬编码的,这不优雅但不是最终的(参见 3. 绑定两个列表)。
 您可以运行应用程序,这两个子组件将打开在 MainComponent中,而MainComponent又打开在SofaBasicContainer中。但是 OrderListComponent没有绑定源,也没有显示所选客户的订单。
- 组件的唯一代码是 MEF 
- 绑定两个列表所有操作都在 MainComponent的PostOpen事件处理代码中完成:使用Sofacontainer SofaComponentList访问这两个组件,并将CustomerListComponent的listViewCustomers用作OrderListComponent的DataContext。
现在应用程序可以正常工作了。
3. 关于视图...
第 1 个项目:容器: SofaBasicContainer
此项目实现了一个完整的视图管理场景:主要功能包括在打开/关闭容器时加载/保存上次使用的视图,以及一个允许创建、加载和删除视图的菜单。
实现此目标所需的步骤如下:
- 资源:导入 PerspectiveManager助手及其关联的PerspectiveName,并调整命名空间。
- 操作触发器:删除了之前在 BaseWindow构造函数中调用OpenComponent以加载CSWPFMasterDetailBinding组件的代码。取而代之的是视图管理:处理Window_Loaded和Window_Closed事件,分别加载和保存上次使用的视图。
- 菜单:添加了两个菜单;第一个菜单允许进行基本功能,如 Create/Delete视图,第二个菜单显示现有视图列表并允许在它们之间切换。在BaseWindow中添加了相关的 C# 代码来处理 GUI 事件并将其转发给PerspectiveHelper类。
第 2 个项目:容器和组件:CSWPFMasterDetailBinding
目前打开的两个组件就像在 WPF Tab 控件中一样。这种呈现方式对于独立窗口(如 MasterComponent 实例)是相关的,但当一个窗口上的操作会修改另一个窗口时则不适用。
此项目使用视图来并排加载两个组件。
- 资源:与 SofaBasicContainer一样,导入PerspectiveManager助手。
- 操作:在处理 SofaCommon PostOpen事件的方法中添加了perspectiveHelper.LoadPerspective("方法,而在“MainComponent")Closed”事件中则添加了perspectiveHelper.SaveCurrentPerspective()。
- 然后运行应用程序:第一次使用 LoadPerspective方法时,视图不存在,因此没有效果。然后从菜单中加载CSWPFMasterDetailBinding组件,用户根据需要进行组织,并在关闭应用程序时通过Closed事件处理保存视图。这会创建视图,该视图成为应用程序的一部分。然后可以删除PostOpen事件处理中的两个sofaContainer.OpenComponent方法,因为组件将自动随视图一起打开。
历史
- 2011 年 9 月 2 日:初始版本



