原文在此 http://richnewman.wordpress.com/intro-to-cab-toc/
Introduction
Microsoft’s Smart Client Software Factory (SCSF) and associated application blocks are excellent pieces of software. However, for a developer coming to them for the first time they can be extremely daunting. There are many things that can be confusing. Firstly we have modules, workitems, workspaces, and shells, all of which sound similar. Then there’s dependency injection, command patterns, event brokers, model-view-presenter patterns and other high-level concepts, none of which are easy to understand. There’s a load of other concepts as well, multiple example applications, and plenty of documentation. Microsoft’s documentation has a very steep learning curve indeed.
译;
微软的Smart Client Software Factory (SCSF) 以及相关的一些应用模块是非常优秀的,然而,对于刚接触到这块内容的开发者来说,会觉得有许多东西会让人非常困惑, 首先,有这样一些听起来似乎有些熟悉的概念workitems,workspaces,shells .还有“依赖注入”,“命令模式” “事件”,mvp模式以及另外一些很抽象的概念,都不是很容易让人理解,还有一些其他的概念,示例应用,和一些文档,微软的文档不是很具有可读性。
I’m going to write a series of short articles that assume you are a developer coming to this for the first time and are confused as I was. To do this I’m going to focus on one or two basic concepts in each article. There’s quite a lot of blogging about CAB/SCSF on the web, but I couldn’t find anything that gave me a quick, basic introduction with a gentle learning curve. This is what I will try to achieve here.
译;
我将写一系列的短小的文章来给那些刚开始学习这块内容,像我当时一样对此感到困惑的开发者提供一些帮助,我们首先从一两个最基本的概念开始。有很多博客介绍过CAB/SCSF 这一技术,但是我找不到有哪个博客可以迅速地给我一个基本的介绍和一个适当的学习坡度,这也就是我要写这一系列博客的原因。
To start I’m going to look at the basic set up of modules and shells in the Composite Application Block. This is the starting point for most SCSF projects. I’m not going to cover WorkItems until my second article as I found them quite confusing initially.
译;
我们首先从CAB里最基本的shells何modules开始说起, 我会在第二篇博客里介绍workitems因为我发现一开始就将它会比较困难。
Requirements
For this example you only need download the code. This contains the CAB dlls it needs to run.
译;
首先你需要下载这个示例的源码,这里有CAB所需要的dll。
Composite Application Block
The starting point for many developers coming to the Composite Application Block (CAB) is the need to either
1. To design a complex user interface that has some kind of separate components that can be developed and released independently, or
2. To take several existing user interface applications and combine them in such a way that they can continue to be developed independently, but appear in the same window with common menus etc and can interoperate on some level.
译,
对于许多开始学习CAB的开发者来说他们最初的目的 无非就是
1,设计一个复杂的可以让不同的组件可以独立开发和发布的用户界面。
2,组合一些已有的用户应用程序接并且使得他们可以独立开发,但是最终和一个公用的菜单一起显示在同一个窗口里,且彼此可以互用。
This is certainly where I started. The key point in both cases is that if we want our components to be released independently then they can’t have direct references to each other. In short we want ‘applications’ or components that appear in the same user interface (same window) with common menus and some ability to talk to each other, but don’t directly reference each other.
The CAB allows us to do this. It clearly offers us a lot more than just this, but in this article I will show the basics.
这就是我要开始讲解的地方,以上两种情况的重点在于如果我们想要我们的组件可以独立的发布,他们彼此之间不能相互引用,简而言之,我们希望我们的应用程序,组件可以同时和一个通用的菜单出现在同一个用户界面(同一个窗体)里,并且他们可以彼此通信,但是彼此绝不相互引用。
Modules and Shells
The first concept you need to understand for this is that of a ‘module’. This is what I am referring to as a ‘application’ or ‘component’ in the paragraph above: a block of code, usually with a user interface that can be displayed in a common window, but which doesn’t reference other modules. In reality these are separate .NET projects within our solution that don’t reference each other.
首先你要理解的一个感念是“module”, 这就是我前面提到的“应用程序”或者说“组件”也就是一个程序块,通过一个程序接口,它可以显示在一个公用的窗体里,但是却彼此不依赖。 事实上,他们是独立的相互不依赖的 .net的项目。
The second concept is that of the ‘shell’. This is simply the ‘common window’ I’m referring to in the paragraph above. It will contain our modules, or at least the visual parts of them. In reality the shell is usually contained in its own .NET project, but one that doesn’t reference any of the modules, nor is referenced by them.
第二个概念是关于“Shell”, 这就是我之前提到的“公用的窗体” 它将会包含我们“module” 或者至少 是module的可视部分。 事实上,这个Shell包含于它自己的.net项目里 但是此项目部引用任何"modules" 并且也不被 "modules"引用。
So, simplistically:
Module = standalone project to be used in a composite user interface
Shell = the host form for the composite user interface
所以,简单地说;
module;在组合的界面里独立运行的项目。
shell, 组合界面的主人。
Our Example
Let’s start with a really simple example. Assume we have a C# project that just displays a red form, and another separate project that just displays a blue form. These are going to be our two modules. Then we want a third project to contain a shell, which in this case will just be another form that we want displayed. To keep this example really simple we won’t try to display the red and blue forms actually inside the shell: this will be done in part 2.
The difficult bit is that although all these projects will be in the same solution, none of them will reference each other directly, yet all three forms will display at start up. Obviously we could do this fairly simply with reflection, but we will use the CAB to do it.
让我们从一个真正简单的例子开始吧, 假设我们有一个c sharp的项目 需要显示在一个红色的form上,同时,另一个独立的项目显示在一个蓝色的界面上, 这就是我们的两个 module, 现在我们需要第三个项目去包含这个 shell, 这是另外一个我们需要显示的form, 为了让这个例子保持真正的简单,我们不会试着让红色和蓝色的form 真正的显示在shell里面, 这我会在part2里介绍。 困难之处在于,虽然这三个项目都在同一个solution里,他们之间彼此互不直接引用,然而所有的三个form都会在一开始的时候就显示出来,很明显,我们可以做一个合适的反射,但是我们会使用cab去实现。
Na?ve Application Example
The code for this example is available.
To implement the example:
1. We set up three projects (all Windows applications) in a solution. We’ll call these Red, Blue, and Shell. For the Red and Blue projects (which will be our modules) we simply change the BackColor property of the form to red and blue as appropriate.
去实现这个例子,我们需要设置我们的三个windows 应用程序在同一个solution里,我们分别叫他们红,蓝,和shell, 对于红和蓝(也就是我们的“modules”) 我们简单的设置他们的背景颜色为红和蓝。
2. We add references to the CAB dlls (Microsoft.Practices.CompositeUI.dll, .Practices.CompositeUI.WinForms.dll, and Microsoft.Practices.ObjectBuilder.dll) to all three of the projects. We also make sure all three build into the same build directory (via the Properties/Build tab). Shell is the startup project.
我们分别给这三个应用程序加入cab的dlls,保证这三个项目都编译到同一个目录,shell是启动项目。
3. Now if we run the projects all three will build, but clearly only the form from the Shell project will get displayed, since that is the start up project. We need to tell the other modules to show their forms. To do this we change Program.cs in Shell (our start up code) to inherit from FormShellApplication<,> as below:
现在,如果我们启动项目,这仨都会编译,但是很明显 只有shell 项目会显示出来,因为他是启动项目, 我们需要告诉其他两个modules 去显示他们的form, 为此,我们改动 shell的 program。cs 去继承 FormShellApplication。
using System; using Microsoft.Practices.CompositeUI.WinForms; using Microsoft.Practices.CompositeUI; namespace Shell { public class Program : FormShellApplication{ [STAThread] static void Main() { new Program().Run(); } } }
The two types passed in to FormShellApplication<,> both get instantiated when Program is instantiated and .Run() is called (this calls into the base classes clearly). Form1 is our shell form and gets displayed, WorkItem I will discuss in part 2. After this change the application will still only display the MDI form, but the .Run() call will enable us to show the other two screens with some other simple changes.
当这两类型传到FormShellApplication里后,当程序实例化的时候他们都被实例化,当run()被调用的时候, 我们的shell form中的form1就被显示了,我将在part2里讨论workitem,在做完了这些后,这个应用程序仍然只会显示mdi form,但是这个run()的调用将使我们能够展示其他两个form只要我们完成另外一些小的改动。
4. In fact the application is now looking for an XML file telling it what other modules to load. Again this happens as a result of the call to .Run(). The XML file has to be called ProfileCatalog.xml, has to be copied always into the output directory, and has to look like the XML below:
事实上,这个应用程序现在正在搜寻一个可以告诉他需要调用哪些其他module的xml文件,这同样是调用run()的结果,这个xml叫做ProfileCatalog.xml 它总是被复制到输出的目录下, xml的结构如下;
We add this file to the Shell project. Now if you get the names of the modules wrong and then run the application it will throw an exception, showing that it’s trying to find the relevant files.
我们把这个file加到shell项目里,现在如果你把modules的名字改了,这个application就会报错
5. Finally we need something in the Red and Blue projects that will actually run as a result of this (the CAB doesn’t just call the usual start up code). We can do this by adding a class that inherits from Microsoft.Practices.CompositeUI.ModuleInit and overrides its Load() method. The CAB code will call this Load method from the .Run() call above:
最后,我们需要在红,蓝项目加一些实际上可以帮助我们启动他们的代码(cab并不会去调用他们正常启动的代码) 我们可以加一个类去继承 Microsoft.Practices.CompositeUI.IModule 并且覆盖load()方法。 当 run()方法被调用的时候,cab方法会去调用这个方法。
using Microsoft.Practices.CompositeUI; namespace Blue { public class BlueModuleInit : ModuleInit { public override void Load() { base.Load(); Form1 form = new Form1(); form.Show(); } } }
That’s it: if we put the code above in the Blue project, and the equivalent in the Red project, the three forms will now load and we have our first composite application. None of the three separate projects has a direct reference to any other.
就是这样,如果我们把以上代码房到蓝和红的项目里, 这三个form会同时装载,这样我们就得到了我们的第一个组合应用程序,并且他们之间是不互相引用的。
Summary
The difficult thing to understand when you see this for the first time is that the CAB is doing a lot of work for us when we call that .Run() method on a FormShellApplication class. It’s instantiating and showing our Form1 (our shell). It’s checking for an XML file called ProfileCatalog.xml. If it finds ProfileCatalog.xml, it is loading the assemblies it finds listed in there. It’s then looking for any ModuleInit classes in them, and calling .Load if it finds them. None of that’s too complicated, but it can feel a bit like smoke and mirrors are being used.
总结,难以理解的地方是,当你第一次看到当我们调用run()的时候 cab帮我们做了很多。它实例化并且显示了我们的form1, 它寻找一个叫profilecatalog的xml,如果他能找到这个file,它读取他们的程序集,然后搜寻它们中的moduleinit 类,然后调用load方法 这些都不复杂。但是在使用的时候却很神奇。
This article is continued in part 2, where I explain WorkItems, and part 3 where I will explain dependency injection, and show how we can get the two forms in the Red and Blue projects in our example to be MDI children in the shell.