Composite UI Application Block学习笔记之Smart Part

  一、简单概念介绍

      CAB提供一个开发环境能很好的隐藏复杂度和提高生产力,通过高度抽象和关注点的分离,开发人员能够关注于业务逻辑提高基础框架代码的复用。Smart Part是整个CAB体系中重要的一部分,它可以将界面独立于业务逻辑,让界面和业务逻辑松散的耦合起来。Smart Part的应用中有几个重要概念:

     WorkSpace:作为一个容器,它可以统一的添加和显示视图。CAB中提供了一组控件作为视图的容器,包括DeckWorkSpace,MDIWorkSpace,TabWorkSpace,WindowWorkSpace,ZoneWorkSpace。它们的作用有点类似普通Windows 控件Panel,MDI窗口,TAB控件,Window窗体。在程序中根据名称可以通过WorkItem的WorkSpace集合索引到它们。

     Smart Part: 也可以被称作View,实际上是一个个自定义的控件。

     其他像WorkItem, State之类的概念我在前两次的学习笔记中曾经介绍过了,这里就不再罗嗦了。

      我们利用CAB中WorkSpace和Smart Part到底可以做些什么呢?WorkSpace可以一致的显示多个控件,Shell 开发人员创建定制的 workspaces 以提供控件周围相似的框架和修饰,共享的布局和定位,眩目的界面切换。下面我们还是通过一个实例来逐步理解CAB之Smart Part应用给我们带来的好处,特别是在多个数据来源,将多个界面块组合成一个统一美观的整体时的应用方便性。

   二、实例研究

1.应用场景

    我相信研究过CAB的朋友肯定知道微软提及的一个应用案例,Dell的客服桌面应用。该应用就是为了提高克服人员的办公效率,将在提供客户服务过程中需要从多个软件系统获取的信息,统一的集成到一个界面上来。也许大家会为该应用卓越的表现力,而感到亲切、人性化,其实类似这样的应用我们通过CAB基础件的应用,也可以达到类似的效果,而不需要在界面化太多的精力去处理业务和界面之间的交互。为了能够说明问题,我在学习过程中也做了一个简单的例子。

     大家可能都开发过网站或者用过Share point portal,那大家肯定都应该知道Web Part这个概念,在CAB中类似的有了Smart Part的概念。Web Part能将一个个的信息块集成到一个统一的Portal,Smart Part也能将不同的信息块集成到一个界面。我们不妨假设这样一个应用场景:

      假设您是某百货店的店长,希望每天能看到店里的销售业绩,能看到自己每天的邮件,能看到自己喜欢的Blog上的资源,能有软件提示自己每天的行程安排,最重要的是这些信息需要在一个统一的界面上显示出来。面对这样的需求,CAB来开发界面大有用武之地,如果采用SOA的架构,更能使遗产系统的价值利用得更好。

2.需求分析

   具体来说该软件有以下需求:

a.销售业绩的信息从店面现有的进销存系统中获得。

b.Blog信息通过RSS获得

c.每天的日程安排情况从秘书给自己设定的OA系统中获得。

d.电子邮件通过集团统一的电子邮件系统中获得,该邮件系统支持Web Service。

e.软件界面简单统一,不能有过多的界面切换,最好能将所有概要信息显示在一个界面之上。

f.软件开发周期要短,尽量利用现有系统的数据和逻辑。

g.界面可能会扩展,以后可能需要集成更多的系统。

     面对这些需求,我们不难得出结论:用CAB基础件开发界面,用Web Service集成遗产系统信息都是不错的选择。

3.开始建立应用

     由于本例我们主要是为了学习CAB,至于Web Service的应用不做阐述。为了模拟类似的效果,和SOA系统开发过程中共享契约的原则,我们通过数据集来代表契约(XSD文件),用实际的XML文件代表Web Service的返回结果。

    第一步:建立解决方案

      a.启动VS2005,新建windows application,命名为TestSmartPart在项目中引用以下组件:

  • Microsoft.Practices.CompositeUI;
  • Microsoft.Practices.CompositeUI.WinForms;
  • Microsoft.Practices.ObjectBuilder

  第二步:绘制主窗口

      a.将系统默认产生的Form1窗体,命名为TestSmartPartForm。

      b.通过菜单-->工具-->自定义工具箱,选择浏览Microsoft.Practices.CompositeUI.WinForms.dll,这是工具箱中能出现DeckWorkSpace,MDIWorkSpace等控件。

     c.打开TestSmartPartForm的设计视图,调整窗体的高度和宽度,绘制5个DeckWorkSpace控件,分别命名为deckWS_Calendar,deckWS_Blogs,deckWS_Plan,deckWS_Task,deckWS_MainEmail。最终的界面效果如下图:


第三步:定义数据实体(或者定义服务契约)

   a.新建DataSources文件夹。

   b.在DataSources文件夹中添加myBlogs,myEmail,myTasks,myPlans四个数据集。具体结构在文中不作描述,大家可以参考文后的源代码。

   c.根据数据集的结构,建立数据源。这里我们用Xml文件来代替数据库中读取的数据,分别添加myBlogs.xml,myEmail.xml,myTasks.xml,myPlans.xml。数据的内容我在此略过。将各个Xml文件内容录入完成后,拷贝到运行目录。我不知道为什么设置文件的Copy To OutPut Dierctory属性为always,VS.Net编译项目的时候无法复制到输出目录,难道该属性是在建立安装程序才起作用?后来我发现是生成的时候连同DataSource文件夹,一起输出到了Bin目录,因此读取的时候也需要加上DataSource目录。

第四步:建立视图

    a.建立BlogView、DailyPlan、DailyTask、Emails四个文件夹,这样做的目的只是为了让项目的结构更加清晰,同时以后要是对这些视图进行扩展,也可以把子视图都放到对应的文件夹里。

   b.在项目根目录下添加UserControl,命名为TitlePart.cs。给该用户控件加上一个lable控件,并且加入以下代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI.SmartParts;

namespace TestSmartPart
{
[SmartPart]
public partial class TitlePart : UserControl
{

public TitlePart()
{
InitializeComponent();
}


/// <summary>
/// Text that will show in the title label.
/// </summary>

public string Title
{
get
{
return lblTitle.Text;
}

set
{
lblTitle.Text 
= value;
}

}


/// <summary>
/// Tooltip for the smartpart.
/// </summary>

public string Description
{
get
{
return toolTip.GetToolTip(lblTitle);
}

set
{
toolTip.SetToolTip(lblTitle, value);
}

}

}

}

 

   c.建立BlogView视图。在文件夹BlogView新建UserControl,命名为myBlogView.cs。将该类从TitlePart继承,将标题设置为“我的博客".加入Grid控件和数据集myBlogs进行绑定。最终效果如下图:

   d.建立dailyPlanView视图。在文件夹DailyPlan中新建用户控件,命名为myPlan.cs。将该类从TitlePart继承,将标题设置为“我的日程"。加入Grid控件和数据myPlans进行绑定。

    e.建立myTaskView视图。在文件夹DailyTask中新建一个UserControl,命名为myTask.cs,将该类从TitlePart继承,将标题设置为“销售报表"。进入myTask.cs的设计时加入控件reportView1,同时根据myTask数据集建立柱状图。最终界面如下图:

    f.建立EmailView视图。为了表现WorkSpace能够层层显示子视图,我们在EmailView视图上,添加了两个DeckworkSpace用来显示邮件的列表视图和详细信息视图。EmailView本身又是通过上一级WorkSpace显示的。这里略去邮件列表视图和邮件详细信息视图的建立过程,请参前面视图建立过程和源代码。

  到此我们基本上完成各个视图的建立,大家不难发现,他们之间是相对独立的,和WorkSpace无关的。

第五步:编写WorkItem和Controller,连接所有视图

   a.建立应用入口类。新建SmartPartApplication.cs,将类代码修改成以下形式:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.WinForms;
using System.Data;

namespace TestSmartPart
{
//注意这句程序,它能将窗体和特定的WorkItem关联。没有没有特定的WorkItem,采用默认的“WorkItem”代替“SmartRootWorkItem”。
class SmartPartApplication : FormShellApplication<SmartRootWorkItem, TestSmartPartForm>
{
/// <summary>
/// The main entry point for the application.
/// </summary>

[STAThread]
public static void Main()
{
new SmartPartApplication().Run();
}

protected override void BeforeShellCreated()
{
base.BeforeShellCreated();
}

 

b.建立根级别的WorkItem。在本例中WorkItem的主要作用是提供显示试图的方法,将业务实体数据填充到数据集,并且让数据集和控件绑定。

以显示BlogView为例,我们来说明WorkItem是如何将BlogView展示到特定的WorkSpace的。首先我们来看以下代码:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.SmartParts;
using Microsoft.Practices.ObjectBuilder;
using System.Data;

namespace TestSmartPart
{
public class SmartRootWorkItem : WorkItem
{

private TestSmartPart.BlogView.myBlogView blogView;

public override void Run()

ShowBlogView();

}



private void ShowBlogView()
{
this.State["BlogDataSet"= new DataSet();
((DataSet)
this.State["blogDataSet"]).ReadXml("myBlogs.xml");

IWorkspace blogWorkspace 
= this.Workspaces["deckWS_Blogs"];
blogView 
= this.Items.AddNew<TestSmartPart.BlogView.myBlogView>("BlogView");
blogWorkspace.Show(blogView);

}


}

}

     系统通过入口程序会自动调用SmartRootWorkItem的Run方法,然后调用ShowBlogView方法来显示Blog视图。我们需要重点关注的是ShowBlogView本身。在显示视图之前我们需要为视图中用到的数据,创建实例并且将其载入WorkItem的State列表,以便在视图和Controller中共享使用。如代码:

this.State["BlogDataSet"= new DataSet();
((DataSet)
this.State["blogDataSet"]).ReadXml("myBlogs.xml");

    最后我们需要将视图,在住窗体的deckWS_Blogs控件中显示。在WorkItem中我们直接可以通过this.Workspace[]来索引住窗体上的Workspace,CAB已经自动将在设计时添加的Workspace控件添加到了Workspaces列表。索引导特定的Workspace后,我们需要将要显示的试图添加到WorkItem的Items集合,最后通过Workspace.Show(view)来显示视图。代码如下:
   

IWorkspace blogWorkspace = this.Workspaces["deckWS_Blogs"];
blogView 
= this.Items.AddNew<TestSmartPart.BlogView.myBlogView>("BlogView");
blogWorkspace.Show(blogView);

   其他视图的添加方法类似,我就不再做说明。
   c.正对根级别的WorkItem,建立对应的Controller。新建RootController.cs,将RootController类从Controller继承,加入以下代码,将其与WorkItem关联。

private SmartRootWorkItem rootWorkItem = null;

[ServiceDependency(Type 
= typeof(WorkItem))]
public SmartRootWorkItem RootWorkItem
{
set
{
rootWorkItem 
= value;
}

}

   同时为了将业务实体和WorkItem、View结合起来,我们将数据集通过State列表共享。如以下代码:

 

[State("BlogDataSet")]
public DataSet BlogDataSet
{
set
{
if (value != null && State != null)
{
State[
"BlogDataSet"= value;
}

}

get
{
return (DataSet)State["BlogDataSet"];
}

}

   RootController类中当然还共享了其他如DailyPlan、DailyTask等业务数据,由于篇幅不再列出,在源代码中可以查看。

第六步:修改视图代码,在视图中显示数据。

     我们还是以BlogView为例来说明,视图是如何将数据显示出来的,其他视图都相似,请参考源代码。在视图中主要是通过私有变量和加有[State]修饰符的属性和业务数据、Controller相关联的。代码如下:

private RootController controller = null;

[CreateNew]
public RootController Controller
{
set
{
controller 
= value;
}

}


private DataSet blogDataSet = null;

[State]
public DataSet BlogDataSet
{
set
{
blogDataSet 
= value;
}

}

第七步:特殊处理,视图的嵌套。

    EmailView视图和其他视图有些不一样,EmailView视图有DeckWorkspace,在DeckWorkspace中还必须显示两个子视图。这样就使数据的共享,WorkItem的访问存在少许差异。我们不妨首先来看SamrtRootWorkItem中显示EmailView的方法:

 private void ShowEmailView()
{
IWorkspace workspace 
= this.Workspaces["deckWS_MainEmail"];
emailView 
= this.Items.AddNew<TestSmartPart.Emails.EmailView>("EmailView");
workspace.Show(emailView);

if (emailWorkItem == null)
{
emailWorkItem 
= this.Items.AddNew<TestSmartPart.Emails.EmailWorkItem>("EmailWorkItem");
}

emailWorkItem.State[
"emailDataSet"= new DataSet();
((DataSet)emailWorkItem.State[
"emailDataSet"]).ReadXml("myEmails.xml");
emailWorkItem.Run();
}

     我们发现TestSmartPart.Emails.EmailWorkItem被加入SamrtRootWorkItem的Items集合,deckWS_MainEmail这个Workspace也被加入SamrtRootWorkItem的Items集合。因此访问这些Workspace时,必须通过WorkItem.Parent.Workspaces[ ]来访问。代码如下:

public class EmailWorkItem : WorkItem
{
private EmailDetailView emailDetailView;
private EmailTitleView emailTitleView;

public override void Run()
{

emailDetailView 
= this.Items.AddNew<EmailDetailView>("EmailDetailView");
emailTitleView 
= this.Items.AddNew<EmailTitleView>("EmailTitleView");

IWorkspace titleWorkspace 
= this.Parent.Workspaces["deckWS_EmailTitle"];
IWorkspace detailWorkspace 
= this.Parent.Workspaces["deckWS_EmailDetail"];

titleWorkspace.Show(emailTitleView);
detailWorkspace.Show(emailDetailView);
}


}

第八步:运行程序

      经过一些修改后,我们可以运行我们的例程,大家看看下面这个界面是不是像一个Portal啊?

源代码下载地址:/Files/hyphappy/TestSmartPart.rar

转贴供学习方便:http://hyphappy.cnblogs.com/archive/2005/11/24/283694.html

posted on 2007-04-18 17:37  吴争容  阅读(582)  评论(0编辑  收藏  举报