Composite UI Application Block开发的介绍

来源:互联网 发布:广联达预算软件购买 编辑:程序博客网 时间:2024/05/29 11:50
 

完成本实验之后,您能够:

·         了解Composite UI Application Block。

·         使用Composite UI Application Block 的Module Loading特性。

·         了解推荐的项目布局和代码结构。

 

场景

您将要集中了解Composite UI Application Block的目的与特性,以及在开发应用程序时如何使用这个应用程序构造块。

练习1: Composite UI Application Block的概念与特性

在本练习中,您将了解到Composite UI Application Block的组成部分,及其相关的概念。接下来的练习重点则是使用Microsoft Visual Studio 2005开发系统和.NET Compact Framework 2.0版来实现这些概念。

介绍

Composite UI Application Block被设计用于构造基于Windows窗体的复杂应用程序。为此,它提供了一套体系结构和实现方案,这个方案使用了在前端, Line–Of-Business(LOB)应用程序中创建的通用模式。使用Composite UI Application Block构建应用程序有以下好处:

·         对体系结构团队来说,能够改善质量和一致性;

·         对于大型开发团队来说,能够提高生产率,缩短产品开发周期;

·         对于操作人员,能够巩固操作结果。

这个应用程序构造块被设计来开发用于移动设备的商务线应用程序的智能客户端。

设计概述。

通过使用独立而相互协作的模块来支持应用程序开发,是Composite UI Application Block的主要设计目标之一。Composite UI Application Block应用程序可以包含以下组件:

·         SmartParts

SmartParts给出了应用程序中的可视元素。这些可视元素与控制它们的应用程序完全独立,可以完全独立的开发。

·         Workspace

Workspace是SmartPart的容器与管理器。它可以控制SmartPart如何显示或者隐藏,以及如何布局。可以创建并使用以下类型的Workspaces:

¡   TabWorkspace:让您在选项页中显示或隐藏控件和SmartParts;

¡   DeckWorkspace:让您在不可见的框架中以层迭的方式显示或隐藏控件和SmartParts。其IWorkspace方法使用类似卡片组的方式实现SmartParts的显示、隐藏和关闭。显示SmartPart使得它在以前显示的SmartPart上面出现。隐藏SmartPart使除它之外最上面的一个SmartPart显示。关闭SmartPart将它删除,并显示除它之外最上面的一个SmartPart。

·         WorkItems

WorkItem是运行时组件的容器,与组件相互协作以完成一个用例。它包括以下代码和类:

¡   初始化用例的启动(或者初始化)代码;

¡   用例中协作的组件共享的状态;

¡   根据状态和其他资源产生行为的Controller类;

¡   与相应Controller和引用的状态进行交互的View类;

¡   释放资源的代码。

·         Infrastructure Services(基础架构服务)

Composite UI Application Block包含一套底层的基础架构服务,可以在应用程序中使用。您也可以根据您的应用程序构建自己的服务来提供基础架构的能力。应用程序构造块提供以下基础架构服务:

¡   Catalog Reader;

¡   Module Loader;

¡   Event Broker;

¡   Commands。

·         Modules

该模块由一系列服务、WorkItems、SmartParts、控件、商务实体和Module Initialization类组成,其中Module Initialization类用于初始化和运行该模块的WorkItems。

·         Module Services

该模块服务是对象,使用Service属性注册一次之后,就可以从任一WorkItem的其他组件中引用它。

·         State

WorkItem包含状态,它是一个松散类型的对象集合,这些对象由该用例中协作的组件共享。状态可以通过调用WorkItem的Load和Save方法进行维持。可以使用实现IStatePersistenceService的基础架构服务在任一存储介质上维持状态。

·         Controllers/Presenters

在MVC和MVP模式中,控制器和表示器分别具有同样的角色。

·         Service Agents

服务代理是用于与外部后端服务进行交互的组件。

当您开发的应用程序要使用外部服务时,使用服务代理就是必要的;所以在这里作为重要组件列出。然而,CAB却并没有为这些组件提供任何正式的结构或者支持。

Composite UI Application Block由一些子系统组成,它们通过互操作提供这个应用程序块对外提供的功能。这些子系统都在自己的命名空间和程序集中定义,如下图所示。

在典型的CAB实现中,会使用到以下程序集:

·         Microsoft.Practices.CF.CompositeUI.WinForms.dll;

·         Microsoft.Practices.CF.CompositeUI.dll;

·         Microsoft.Practices.CF.ObjectBuilder.dll。

 

 

练习2: 创建一个模块

在本练习中,创建一个基础模块,只包含一个服务和一个WorkItem。

第一步

双击打开桌面上的ModuleLoader.sln文件,并生成该解决方案。

设置环境

1.      在视图中展开所有的项目和文件夹,熟悉一下Visual Studio 2005中新的Solution Explorer。

特别检查一下Services、WorkItems和Views文件夹和结构。在使用CAB开发应用程序时,这是组织和布局您的解决方案和项目的一种方法。您在每个模块自己的.NET类库项目中编写程序(并且各模块分别编译为一个程序集)。所有的服务、WorkItems和Views都分别在自己的文件夹中。ModuleInit类在根目录下,保存一个初始化该模块的类。

您可以看到一个淡黄色,名叫CAB的文件夹。这是微软引入IDE的一种新特性,叫做解决方案文件夹。这些文件夹用于分组这些项目和解决方案项目,以便能够更好的管理和查看这些解决方案。CAB文件夹包含Composite UI Application Block的这三个子系统。

注意,您需要关注两个项目:ModuleLoaderShell和GPSModule。

ModuleLoaderShell项目包含应用程序的入口点和Workspaces。GPSModule是组成所要创建模块的结构体。确保在项目属性的Device选项卡中,GPSModule的输出文件的文件夹字段的值和ModuleLoaderShell程序集保持一致;并且对于ModuleLoaderShell和GPSModule,在解决方案属性(右键点击解决方案->属性)的配置属性节点中Deploy复选框是选中的。

2.      在GPSModule项目下展开References节点。

您可以看到这四个CAB子系统(Mobile.CompositeUI、Configuration、Mobile.ObjectBuilder和Mobile.CompositeUI.WinForms)的引用。设置每个引用的Copy Local属性为true,这样,在编译该解决方案(包括子系统)时,这些程序集将被拷贝到本地的模块与外壳的调试目录下。

生成您的第一个模块

1.      展开Services文件夹,打开IGPSService.cs和GPSService.cs文件。

模块可以包含多个不同的元素。在本练习中,所创建的模块有两个服务,一个视图和一个驱动服务和视图的WorkItem。

2.      在IGPSService.cs文件中标识的位置粘贴如下代码。

public interface IGPSService

{

int GetLatitude();

int GetLongitude();

}

创建服务的第一步是要创建定义服务操作或者方法的接口。在本练习中,所申明的服务有两个主要的操作:GetLatitude和GetLongitude。使用接口来定义服务,因为这样您可以很容易改变操作的实现,同时又不影响其他部分的基础代码。

3.      在GPSService.cs文件中标识的位置粘贴以下服务实现代码。

[Service(typeof(IGPSService))]

public class GPSService : IGPSService

{

public int GetLatitude()

{

return 42;

}

public int GetLongitude()

{

return 125;

}

}

这是服务创建中很重要的一部分。它实际实现您通过IGPSService接口定义的服务。您创建了一个名为GPSService的类,它实现IGPSService接口。然后,需要为您的两个操作编写实现代码:GetLatitude和GetLongitude。在实现方法中,仅仅各自返回一个虚拟的值。然而,有的时候,您可能想要重新编写这个服务的实现,真正连接到GPS接收器上,获取实时的位置信息。

您会看到这段代码和普通的接口实现代码有些不同,首先就是在类的上面使用了Service属性。这个属性用于将这个类注册为当前模块的服务。这样,CAB才能够轻易得定位到服务的实现,并在必要的时候使用它们。这里,您把ServiceType设置为IGPSService接口的类型,通过这种方式,您可以轻易的改变代码实现,而不会影响到那些需要引用该服务的代码。

4.      打开Services文件夹下面的OnDemandService.cs文件。在第一个“//TODO”标识下面粘贴以下代码。

public interface IDistanceCalculatorService

{

int ComputeDistance(int latitude, int longitude);

}

这里,您创建了另一个名为IDistanceCalculatorService的服务,它有一个操作,通过经度和纬度的值,计算距离。

5.      在第二个“//TODO”标识下面粘贴以下代码。

[Service(typeof(IDistanceCalculatorService), AddOnDemand = true)]

public class DistanceCalculatorService : IDistanceCalculatorService

{

public DistanceCalculatorService()

{

MessageBox.Show("This is DistanceCalculatorService being constructed
for the first time.\nSubsequent calls to the service will not require construction.",

"Service constructed",

MessageBoxButtons.OK,

MessageBoxIcon.None,

MessageBoxDefaultButton.Button1);

}

 

public int ComputeDistance(int latitude, int longitude)

{

return 1234;

}

}

6.      这一次,您编写的是IDistanceCalculatorService的实现。您还编写了一个构造函数,当服务被构造和初始化时,会弹出对话框提醒您。

7.      这第二个服务引入了一个叫做On Demand Services的特性。使用On Demand Services特性实现的服务,直到实际请求使用时,才会被实例化。要使用On Demand Services特性,需要添加AddOnDemand属性,并设置其值为true。

不像其他服务那样在模块初始化时就被创建,on-demand的服务直到代码定位到该服务才会被创建。当您有很多服务的时候这种方法十分有用,因为只有在需要使用服务的时候,才会把它们装载到内存中。例如,您可能会有200个服务,只有需要时才使用,这将节省很多内存,可以极大的改善性能。

8.      现在,服务都已经有了,您需要设置视图来使用前一步实现的服务。

展开Views文件夹,右键点击GPSView项,选择View Code。

9.      您可以看到一个名叫GPSView的类,它是从UserControl继承的。这和其他自定义的控件实现一样。如果您不熟悉.NET Framework 2.0,您可能会对声明部分的partial关键字感到陌生。而且,代码中有对InitializeComponent方法的调用可是却找不到InitializeComponent方法的定义。

.NET Framework 2.0的一个新特性允许您把一个类做成partial的。这样,您可以在当前的代码位置只编写一部分实现代码,而在其他位置编写余下的实现代码。您可以把一个类的定义划分为多个源文件来实现。这可以帮助您消除混乱,在一个文件中实现UI的布局,而在另一个文件中实现您的事件处理函数。例如,发送一个文件给创建前端程序的视图设计员,而发送另一个文件给创建业务逻辑和事件处理代码的业务逻辑开发人员。

在GPSView类的头部,第一个标识 “//TODO”的地方,添加如下代码。

private IGPSService gpsService;

 

[ServiceDependency]

public IGPSService GPS

{

set { gpsService = value; }

}

这部分代码告诉CAB,这个类依赖于IGPSService。您没有初始化一个新的服务。您要使用的是已经创建和存储的服务。CAB使用Inversion of Control(IOC)或者Dependency Injection技术来实现该任务。当您在成员属性中放置了[ServiceDependency]属性后,当视图添加到WorkItem时,将会自动完成对正确运行时要调用服务的初始化工作。

在您的视图中要使用到GPS服务,所以就在这里添加了依赖型。

10.    用以下代码替换cmdGetDistance_Click方法。

private void cmdGetDistance_Click(object sender, EventArgs e)

{

// 注意,您没有为IDistanceCalculatorService添加ServiceDependencyAttribute,

// 因为,这样这个服务会在视图添加到工作项时被创建。

// 这仅仅是为了这个实例而已。

// 该服务直到这个方法被调用才会被装载到内存。

// 直到属性被第一次调用,才会应用[ServiceDependency]属性。

 

IDistanceCalculatorService calc =parentWorkItem.Services.Get<IDistanceCalculatorService>();

txtDistance.Text = calc.ComputeDistance(

gpsService.GetLatitude(),

gpsService.GetLongitude()).ToString();

}

这段代码是响应GetDistance按钮的事件处理函数,点击该按钮可以查询当前的距离。

要得到距离,需要用到IDistanceCalculatorService和GPSService。IdistanceCalculatorService已经定义为了On Demand的服务,它直到现在才会被构造。构造和定位这个服务,需要调用父工作项中Services集合的Get方法。Get方法得到您所要定位服务(IDistanceCalculatorService)的类型,并返回一个引用。

然后,您需要调用ComputeDistance方法来定位实际的距离,并将它存储在视图的txtDistance文本框中。ComputeDistance方法使用经度和纬度作为参数;因此,您还需要调用GPSService的GetLatitude和GetLongitude方法来返回信息。

您不需要调用Services集合的Get方法来定位GPSService。这是因为您使用了[ServiceDependency]属性,当这个视图被添加到WorkItem时,它会为您调用Services.Get方法。

11.    用以下代码替换cmdGetLatitude_Click方法。

private void cmdGetLatitude_Click(object sender, EventArgs e)

{

txtLatitude.Text = gpsService.GetLatitude().ToString();

}

这是响应GetLatitude按钮的事件处理函数,用来从GPSService中获取经度值。在前一步骤,调用了GPSService的GetLatitude方法,并将结果存储在文本框中。您不必调用Services集合的Get操作,因为您已经通过使用ServiceDependency属性获取了这个服务的一个实例。

12.    展开WorkItems文件夹,并打开GPSWorkItem.cs文件。

13.    用以下代码替换OnRunStarted方法:

protected override void OnRunStarted()

{

base.OnRunStarted();

 

IWorkspace GPSWorkspace = Workspaces["mainWorkspace"];

GPSWorkspace.Show(Items.AddNew<GPSView>());

}

这个WorkItem的目的是在指定工作区显示GPSView。

WorkItem是该体系结构中至关重要的一个部分,这里说的是在外壳的workspace中显示视图的方法。

如果您对.NET framework 2.0还不熟悉,您可能会对以下语句产生疑惑。

Items.AddNew<GPSView>().

在平台中引入了一种叫做Generics的新特性。您可以访问以下网址,阅读MSDN上的介绍文章以获取更多关于Generics的信息:

http://msdn.microsoft.com/msdnmag/issues/06/00/NET/

AddNew调用只是把类型作为模板参数接收,这说明它是什么类型,用什么类型工作。然后它将返回这个类型,而不是简单的对象类型,这样就不必做类型转换了。

创建Shell

1.      如果还没有展开ModuleLoaderShell项目,则将它展开,然后双击MainForm.cs项。

展示在您眼前的是新的Visual Studio 2005窗体开发环境。在窗体中,您可以看到一个TabWorkspace控件,名为mainWorkspace。现在,您可以明白在上一步骤中GPSView被放到哪里了。请查看上一步,您会发现您已经引用了这个mainWorkspace,控件,相关的语句是:

IWorkspace GPSWorkspace = Workspaces["mainWorkspace"];

2.      在Solution Explorer中,双击ProfileCatalog.xml文件以打开它。

Composite UI Application Block支持通过使用解决方案类别配置文件来自定义应用程序。解决方案配置文件提供一个模块的列表,被装载到应用程序中。

因为您已经创建了一个模块,并且希望在应用程序中使用,您必须在ProfileCatalog.xml文件中定义该模块。这样,模块装载器才会装载这个模块。

3.      用以下代码替换ProfileCatalog.xml中的XML内容:

<?xml version="1.0" encoding="utf-8" ?>

<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile" >

<Modules>

<ModuleInfo AssemblyFile="GPSModule.dll"/>

</Modules>

</SolutionProfile>

研究一下格式:其实很简单。您可以为应用程序定义多个不同的解决方案配置文件,但是要记住,每个应用程序实际只能使用一个配置文件。您可以看到,在前面这个XML示例中,解决方案配置文件的名字是默认的。它是默认的配置文件,如果没有在代码中指定,使用的就是这个配置文件,而不是其它。

确保Copy To Output Directory属性设置为Copy always,否则,解决方案就不知道应该在哪里去找这个类别文件。

每个解决方案配置文件可以有多个模块,但是在这个例子中,您只有一个名为GPSModule的模块。

创建Program和根WorkItem

1.      在Solution Explorer中,双击Program.cs文件以打开它。

2.      用以下代码替换Main方法。

[MTAThread]

static void Main()

{

new Program().Run();

}

这是这个应用程序的入口点。

调用这个类的Run方法。这个类实现了FormShellApplication<ShellWorkItem, MainForm>,并且给它传入了要使用的工作项类型,以及作为外壳要使用的窗体。

调用Run方法将会装载ProfileCatalog.xml文件,并决定需要装载哪个模块。在获取模块列表之后,它会开始装载这些模块并对它们进行初始化。如果有模块包含了ModuleInit类,将会构造这个类,并调用相应的Load和/或AddServices方法。

试运行程序

1.      要生成该应用程序,按Ctrl+Shift+B。

这将生成这个解决方案。修改生成过程中可能出现的错误,然后重复这个步骤,直到不再显示任何错误。

2.      要以调试模式启动该应用程序,按F5。

警告 模拟器明显比实际设备要慢得多,所以在启动应用程序的时候需要等一会。

而且,如果您还不能生成和调试该解决方案,请确保ModuleLoaderShell项目已经被设置为Startup Project了。

如果在这个阶段出现错误,请确保您的ProfileCatalog.xml文件是正确的。当出现Connect to Device窗口时,选择Windows Mobile 5.0 Pocket PC Emulator并点击Connect按钮。

3.      当UI装载完之后,模拟器中显示的结果如下图所示。

4.      点击GPS Module标签,如下图所示。

 

5.      点击“Get Distance”按钮以初始化IDistanceCalculatorService服务,并调用ComputeDistance方法。

您会收到服务正在第一次被构造的确认信息。

6.      再次点击“Get Distance”按钮。这次将不会收到任何信息。

这说明代码没有试图再次构造该服务。它只是简单的返回存储在根WorkItem中的服务。

7.      点击“Get Latitude”按钮,运行IGPSService服务中的GetLatitude方法。您不会收到任何消息,因为IGPSService服务已经在应用程序启动时被初始化了,这是因为您并没有为它使用On Demand属性。

8.      在Visual Studio 2005中点击Stop按钮,退出该应用程序。

 (完)


 

原创粉丝点击