Composite UI Application Block 学习笔记
1. 重要对象解析
1.1. WorkItem
在WorkItem中,有如下的成员变量:
private WorkItem parent;
private ManagedObjectCollection<WorkItem> workItemCollection;
表明在这个设计中,WorkItem支持多级嵌套,我们可以想象一个程序是由众多的WorkItem组成,而这些WorkItem是由一个树形结构组织在一起,就好比VS2005的选项设置一样,如下图所示:
图 1?1 workitem的树形结构
下面这张图片可能形象一些
图 1?2 WorkItem的组织结构
RootWorkItem可以说是CAB的根本,在它身上承载了太多的资源,比如Shell’s Workspaces,,各种Service,以及各种UI Extensions。下面简单说说RootWorkItem的创建过程。
在CabApplication<TWorkItem>中,导火索是从Run()方法开始,程序代码如下
/// <summary>
/// Starts the application.
/// </summary>
public void Run()
{
RegisterUnhandledExceptionHandler();
Builder builder = CreateBuilder();
AddBuilderStrategies(builder);
CreateRootWorkItem(builder);
IVisualizer visualizer = CreateVisualizer();
if (visualizer != null)
visualizer.Initialize(rootWorkItem, builder);
AddRequiredServices();
AddConfiguredServices();
AddServices();
AuthenticateUser();
ProcessShellAssembly();
rootWorkItem.BuildUp();
LoadModules();
rootWorkItem.FinishInitialization();
rootWorkItem.Run();
Start();
rootWorkItem.Dispose();
if (visualizer != null)
visualizer.Dispose();
}
}
在Run()方法中,首先创建了一个builder对象,然后New了一个RootWorkItem,当然创建过程没有这么简单,它还需要借助ObjectBuilder的力量进行各种依赖注入,所以先通过AddBuilderStrategies加入几个Strategy,这些策略都是对Workitem进行初始化用的,然后通过调用rootWorkItem.BuildUp()完成各种依赖注入。
在这里需要注意的是builder对象在整个运用程序执行期间只有一个实例,为什么这么说呢,我们再看workitem的源代码,
private void InitializeFields()
{
if (builder == null)
builder = parent.builder;
if (locator == null)
locator = new Locator(parent.locator);
if (!locator.Contains(typeof(ILifetimeContainer), SearchMode.Local))
locator.Add(typeof(ILifetimeContainer), lifetime);
ObjectBuiltNotificationPolicy policy = builder.Policies.Get<ObjectBuiltNotificationPolicy>(null, null);
if (policy != null)
{
policy.AddedDelegates[this] = new ObjectBuiltNotificationPolicy.ItemNotification(OnObjectAdded);
policy.RemovedDelegates[this] = new ObjectBuiltNotificationPolicy.ItemNotification(OnObjectRemoved);
}
LocateWorkItem(typeof(WorkItem));
LocateWorkItem(GetType());
status = WorkItemStatus.Inactive;
}
可以看出,所有子节点的workitem都是从父节点把一些关键的资源传递下来,其中就有刚才说的builder对象, 哦,单件模式还可以这样实现J
值得关注的另一个焦点应该就是Locator了,Locator本身也支持嵌套,不过不像WorkItem那样到处开枝散叶,它只支持简单的层次结构,如下图所示:
图 1?3 简单嵌套
也可以通过代码来印证:
public abstract class ReadableLocator : IReadableLocator
{
private IReadableLocator parentLocator;
……
}
从workitem的初始化代码中可以看出,
if (locator == null)
locator = new Locator(parent.locator);
workitem对象总是从它的父节点那把Locator给继承了下来,(这里所说的继承实际上只有持有一个引用而已,只是为了说明上的直观性。)这样也就实现了资源的层层查找,这也就是SearchMode 的意义所在了。
在程序中,如果要取得RootWorkItem,可以用下列代码获取:
context.Locator.Get<WorkItem>(new DependencyResolutionLocatorKey(typeof(WorkItem), null)),为什么可以这样取得,是因为在workitem的初始化代码中做了设定。
private void InitializeFields()
{
……
LocateWorkItem(typeof(WorkItem));
LocateWorkItem(GetType());
……
}
private void LocateWorkItem(Type workItemType)
{
DependencyResolutionLocatorKey key = new DependencyResolutionLocatorKey(workItemType, null);
if (!locator.Contains(key, SearchMode.Local))
locator.Add(key, this);
}
1.2. CabApplication
在CabApplication对象的众多方法中,最重要的莫过于Run方法了,而在Run方法中,最重要的莫过于RootWorkItem的创建。
/// <summary>
/// Starts the application.
/// </summary>
public void Run()
{
RegisterUnhandledExceptionHandler();
Builder builder = CreateBuilder();
AddBuilderStrategies(builder);
CreateRootWorkItem(builder);
IVisualizer visualizer = CreateVisualizer();
if (visualizer != null)
visualizer.Initialize(rootWorkItem, builder);
AddRequiredServices();
AddConfiguredServices();
AddServices();
AuthenticateUser();
ProcessShellAssembly();
rootWorkItem.BuildUp();
LoadModules();
rootWorkItem.FinishInitialization();
rootWorkItem.Run();
Start();
rootWorkItem.Dispose();
if (visualizer != null)
visualizer.Dispose();
}
}
首先创建一个Builder对象,在创建builder对象时,需要注意的是加入了若干个Strategy,
private Builder CreateBuilder()
{
Builder builder = new Builder();
builder.Strategies.AddNew<EventBrokerStrategy>(BuilderStage.Initialization);
builder.Strategies.AddNew<CommandStrategy>(BuilderStage.Initialization);
builder.Strategies.Add(new RootWorkItemInitializationStrategy(this.OnRootWorkItemInitialized), BuilderStage.Initialization);
builder.Strategies.AddNew<ObjectBuiltNotificationStrategy>(BuilderStage.PostInitialization);
builder.Policies.SetDefault<ISingletonPolicy>(new SingletonPolicy(true));
builder.Policies.SetDefault<IBuilderTracePolicy>(new BuilderTraceSourcePolicy(new TraceSource("Microsoft.Practices.ObjectBuilder")));
builder.Policies.SetDefault<ObjectBuiltNotificationPolicy>(new ObjectBuiltNotificationPolicy());
return builder;
}
很显然,CAB提供的若干个显著特征都包含在这些个Strategy中,我们先来说说RootWorkItemInitializationStrategy,它是专门用于初始化RootWorkItem的,并提供一个回调。(callback)
/// <summary>
/// See <see cref="BuilderStrategy.BuildUp"/> for more information.
/// </summary>
public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
WorkItem wi = existing as WorkItem;
if (wi != null && wi.Parent == null)
callback();
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
注意这行代码
if (wi != null && wi.Parent == null)
确保只对RootWorkItem起作用。
这里提供的回调函数则为OnRootWorkItemInitialized,不过在这里只是一个虚方法,
/// <summary>
/// May be overridden in a derived class to perform work after the root <see cref="WorkItem"/> has been fully initialized.
/// </summary>
protected virtual void OnRootWorkItemInitialized()
{
}
它的作用就是被子类重载,比如在CabShellApplication中,
/// <summary>
/// Creates the shell.
/// </summary>
protected sealed override void OnRootWorkItemInitialized()
{
BeforeShellCreated();
shell = RootWorkItem.Items.AddNew<TShell>();
AfterShellCreated();
}
用来建立一个Shell,也就是我们赖以生存的主窗体。
1.3. Module
WorkItem通常位于Module中,在一个Module加载的时候,首先取得Root WorkItem的一个引用,刚才我们看到,Root WorkItem是被Application Shell创建(在程序运行之初),Shell然后加载Module,接着把其中的所有workitem都加入到RootWorkitem旗下。最终组成一支浩浩荡荡的WorkItem大军。
我们来看一个示例:
public class BankTellerModuleInit : ModuleInit
{
private WorkItem workItem;
[InjectionConstructor]
public BankTellerModuleInit([ServiceDependency] WorkItem workItem)
{
this.workItem = workItem;
}
public override void Load()
{
AddCustomerMenuItem();
//Retrieve well known workspaces
IWorkspace sideBarWorkspace = workItem.Workspaces[WorkspacesConstants.SHELL_SIDEBAR];
IWorkspace contentWorkspace = workItem.Workspaces[WorkspacesConstants.SHELL_CONTENT];
BankTellerWorkItem bankTellerWorkItem = workItem.WorkItems.AddNew<BankTellerWorkItem>();
bankTellerWorkItem.Show(sideBarWorkspace, contentWorkspace);
}
其中
[InjectionConstructor]
public BankTellerModuleInit([ServiceDependency] WorkItem workItem)
{
this.workItem = workItem;
}
就是获取一个RootWorkItem。
然后在Load()方法中,则是完成把workitem加入到RootWorkItem的工作:
BankTellerWorkItem bankTellerWorkItem = workItem.WorkItems.AddNew<BankTellerWorkItem>();
2. 重要机制解析
2.1. 用户验证
验证方法存在于CabApplication的Run方法中,
/// <summary>
/// Starts the application.
/// </summary>
public void Run()
{
……
AddServices();
AuthenticateUser();
……
}
private void AuthenticateUser()
{
IAuthenticationService auth = rootWorkItem.Services.Get<IAuthenticationService>();
if (auth != null)
auth.Authenticate();
}
从上面可以看出,实际上只要我们创建一个实现了IAuthenticationService接口的类,然后加入到服务集合中,就能顺顺当当的调用我们自己的实现了。
范例代码如下所示:
protected override void AddServices()
{
base.AddServices();
base.RootWorkItem.Services.AddNew<MobileCabAuthenticationService, IAuthenticationService>();
……
}