Sunday, 17 February 2008
Still here
I hope to have everything sorted in a few days. I have been working a lot on CAB projects again, so hopefully I'll have more to talk about.
Tuesday, 29 January 2008
Composite UI Application Block - Part Three
I know I promised to add another post a few months ago - I haven't managed to find the time until now.
Since then, I've started a new job which doesn't involve CAB at all. I still work on projects at home using CAB, but not as much as I'd like to.
I'm still confident the information I provide here will be accurate, but feel free to point out any mistakes I make.
Anyway, in my last post I talked about the main elements that make up CAB:
WorkItem, SmartPart, Workspace, Service and Event Broker.
I will now try to describe how these parts work together.
Your application could contain many different WorkItems - For example, an application I have worked on contains 3 WorkItems: Customers, Products and Orders.
WorkItems follow a tree structure - There is a single Root WorkItem, and all other WorkItems are descendants of this WorkItem.
Each WorkItem contains one or more SmartParts which are user controls that are displayed to the end user.
The Customers module contains a 'View Customer' form, 'Edit Customer' form and a 'List Customers' form. Each of these forms is a SmartPart.
When the application first loads, the modules registered with the application are instantiated.
All modules inherit from the ModuleInit base class.
It is up to the Module class to create an instance of a ControlledWorkItem - This is a generic class provided by CAB that represents a WorkItem that uses a ModuleController to perform it's business logic.
The module then invokes the Run() method of the ModuleController, which extends and modifies the toolstrips/menus/services associated with the application.
The ModuleController finally tells the module to add any default views to the application (via AddViews()).
So at a minimum, a standard Module is made up of two classes:
- The Module class, which specifies the Views to be added.
- The ModuleController class, which controls the Module and adds UI menus/toolstrips and services.
Back to the example I was using earlier -
My Customer module loads 'List Customers' as a default view. This is done with the following code inside the AddViews() method of the Module class:
CustomerList view = workItem.SmartParts.AddNew
this._rootWorkItem.Workspaces["MainDockingWorkspace"].Show(view);
CustomerList is a SmartPart (ie: View). The first line creates a new instance of CustomerList and adds it to the current WorkItem (the Customer WorkItem). The second line then displays the view in the MainDocking Workspace. (I'll explain how these are added later). The MainDockingWorkspace is part of the RootWorkItem, so we must reference that WorkItem to retrieve the correct workspace.
This view is then displayed to the user. Each view has a Presenter - This Presenter is responsible for performing the business logic of the view.
A Workspace is anything that can display a UserControl. You can create your own Workspaces by simply implementing the IWorkspace interface. The Workspace is responsible for not only displaying the View, but deciding how to display it. For example, a TabWorkspace can display a single view, but switch between open views using a tab menu.
The shell application usually configures the workspaces that are available to use. For example, I have created a Windows Form that contains a DockingWorkspace. This DockingWorkspace is added to the RootWorkItem's Workspace collection.
This is also where you should configure any application-wide services (such as the UI Adapter Factories/Extension Sites).
In my application, I have created multiple 'Shells'. Each shell is looks completely different, but they all contain Workspaces/ExtensionSites (registered with the same names). This allows me to switch the look and feel of an application by simply swapping one Shell with another.
I will post more about Services and EventBroker tomorrow - hopefully this is enough for now to keep everyone interested.
Friday, 21 September 2007
Sorry
I didn't realise people were reading and commenting on this blog, so sorry if you were expecting more posts from me sooner.
I've been working on other projects lately, and haven't spent a lot of time with CAB - so I'll read over these posts and work on Part Three tonight. Expect to see some more in a day or two.
Thanks!
Thursday, 10 May 2007
Composite UI Application Block - Part Two
WorkItem
This is the most important element in CAB. It is basically a container that manages all the objects required to accomplish a certain task.
For example, I have a WorkItem for managing Products. It manages all of the commands/views and presenters required to list, view and edit Products.
The Views might be ProductsListView and ProductsDetailsView. WorkItem also contains state objects to manage the state of the workitem (eg: What product has been selected?).
The workitem contains all of the objects necessary to complete the task - this means the presenters/views never have to look outside of the WorkItem.
Every module created will contain a WorkItem. The WorkItem is controlled by a class called the WorkItemController which is used to initialize and run the module/workitem.
There is a RootWorkItem which is the toplevel WorkItem. All other WorkItems are connected through the root WorkItem.
SmartPart
SmartParts are the visual components of a module (basically a view). They are usually a UserControl that is shown somewhere on the main form.
WorkItems can contain one or more SmartParts to display. It is up to the WorkItem to decide when/where to display the SmartPart.
Workspaces
Workspaces are UI controls that can display SmartParts. A workspace decides how to display the SmartPart (eg: DockedPanel, Frame,Tab etc). WorkItems have a Workspace collection that contains all of the Workspaces it has available to use. WorkItems then display a SmartPart by adding it to a Workspace.
Workspaces need to be added to a WorkItem. These Workspaces are then visible to this WorkItem, as well as all of it's children WorkItems.
You can create a Workspace from any type of control by implementing the IWorkspace interface.
Services
The WorkItem class includes a Services collection that you can use to register/access services. A service is registered as an interface type - This allows you to change the implementation of the service without affecting anything else. For example, you could register a database access service, and then switch to another type of database at a later date.
You can use services for things such as logging, database access etc.
Event Broker
The event broker allows you to create events and have classes publish/subscribe to these events without knowing about one another. An event have one or more publishers, as well as one or more subscribers. It's a way to pass events and data around the application without having to set dependencies.
I think these are the parts of CAB that I use the most. In my next post, I will explain how these fit together and post some guidelines to follow when developing a CAB application. These guidelines will make debugging/future modifications easier and should keep your entire project well structured and clean.
Wednesday, 9 May 2007
Composite UI Application Block - Part One
I was looking for a way to build an application that could load multiple modules and merge them all together seamlessly. I also wanted to have the ability to easily switch data providers at a later date, meaning that I could integrate any number of accounting/product databases into my application.
I read that CAB allows you to seperate your code into independent modules. These modules could interact with each other and the main shell without having to set dependencies. At this point, I realised that CAB was exactly what I was looking for.
CAB has a steep learning curve - it took me a while to work out how the modules fit together.
I've decided to write a series of posts to keep a reference of what I know and to help other developers get started.
What is cab?
Rather than rewrite a definition, I've "borrowed" the one from Cabpedia.com.
(Cabpedia is an excellent resource for learning more about CAB. Many of the questions I initially had were answered there.)
CAB stands for the Composite UI Application Block, and it's a set of .NET 2.0
classes designed to make it easier to build complex Windows Forms
applications. Perhaps the most important way it helps you simplify your
applications is through decoupling the pieces of code that make up a large
application.
CAB also provides support for decoupling the elements
in the user interface. This means you can often make major changes to how
the UI is put together without having to change any of the modules that make up
an application. For example, you might have an Outlook-style user interface
and then decide you want to change it to a web-style interface. No problem.
Just change the UI shell and the modules will load themselves into the new
locations as you've defined it.
When you start a CAB application, you start by creating a UI shell. This is the main form that might include the menu, toolbars, etc.
Next you add one or more Workspaces to the form. These are the locations where the different elements of the UI will appear when the application is running.
Finally, you create one or more modules that contain either elements that will appear in the UI, or Services that will be available for use by other classes.
CAB is perfect for building modular applications. Extending your application is as simple as adding the new module's compiled DLL to the ProfileCatalog.xml file.
Getting Started
It took me a while to get CAB installed and working properly.
Here are the downloads required:
Guidance Automation Extensions - Download
Guidance Automation Toolkit - Download
Composite UI Application Block - Download C# Download VB
Enterprise Library - Download
Smart Client Software Factory - Download
All these toolkits/libraries are used by CAB. Download and install them in the order above. It shouldn't take more than 30 minutes to successfully install everything.
Once these are installed, have a look at the Hands On Labs and this Hello World tutorial from Cabpedia.com.
There are many elements that make up CAB.
In my next post, I will list these elements and explain a bit more about them and what they do.
Monday, 7 May 2007
CAB - UIExtensionSite
I took this explanation from Szymon Kobalczyk's Blog.
You can view the original post here:
http://geekswithblogs.net/kobush/archive/2006/01/24/66946.aspx
The problem that I've found with UIExtensionSites (and it seems a lot of other developers have run into this problem too) is that you need to have a reference the UI item class.
For example:
After registering a menustrip with the ID of "menuBar" I want to add a log in button.
ToolStripMenuItem loginButton = new ToolStripMenuItem("Login");
UIExtensionSites["menuBar"].Add(loginButton);
You can see that we obviously need a reference to the ToolStripMenuItem class, while the MenuBar Add function is handled by CAB.
This will cause problems when at a later date, when I want to update this simple toolstrip menu to an Infragistics powered menu. I will have to go through and modify each and every control that add MenuItems to the extension site.
This just contradicts the reasons why I decided to use CAB - to develop applications using independent modules.
I have known about this limitation for a while, but kept thinking "I'll deal with it when it becomes a problem". Today, it did. I've been working for a couple of weeks on create a docking workspace and getting the user interface working properly and it was almost ready to be used when I was asked to display documents as popup windows instead of the docked panels.
I don't want to remove the docking workspace from the project. The application is built to be used by different divisions within the same company, all of which have different tasks to perform. I don't think the popup window would be suitable for all divisions so I want to leave it as an option. And also, I don't want to see all that development time wasted.
The problem with the popup windows is that the toolbar system I was using before just won't work well with popups. So I will have to change this, BUT if I'm going to leave the docking workspace in as an option for users then I want to use the current toolbars instead.
So what I need is a single menu item control that can be added to any extension site and integrated seamlessly into the UI, no matter what type of menu control the extension site is registered to.
While researching, I found some other people had run into the same problem and pointed out another limitation.
We could use some generic class (instead of ToolbarMenuItem) to define the name, description, icon and click events etc of the menu item, and create an ExtensionSite that reads this definition and turns it into a menu control.
This would work fine for most simple toolbars and we could add/remove/swap toolbars without having to modify the individual modules.
But what about the advanced controls contained in 3rd party UI suites? For example, we want to add a Color Picker - but this isn't supported by all types of toolbars.
I'm sure theres hundreds of similar examples. We could add a generic color picker class and have it display as a textbox in unsupported menus.
But it would be impossible to accommodate for every type of menu item.
What is common among all types of menu?
They all expose some kind of Click event.
Using the color picker example, we could do something like this:
Create the interface to map the 3rd party menu to the generic UIExtensionSite.
Create the generic MenuItem, but tell it we want to pass an instance of one of the native .NET classes to the Click EventHandler - in this case: System.Drawing.Color.
In the ExtensionSite interface, we see that this button is looking for System.Drawing.Color, and knowing that the 3rd party menu supports it - displays a Color Picker.
Other menus will see this and because they don't support System.Drawing.Color, they will find another way to display this - by checking the TypeConverterAttribute and converting to a type that they do support.
This is just a quick idea I've come up with. I find it easier to solve problems while writing my thoughts down.
I still haven't tried implementing it yet but I am planning to. Expect to see a new post in the next couple of days.
TMS
I am building an application called TMS. The main purpose of this application is to speed up the sales process, from creating the sale to dispatching the goods.
I have developed the application using the Composite Application UI Block (CAB), which has significantly cut development time (after getting over the learning curve).
The application is split into many smaller modules.
The main exe is just a shell that hosts these other modules.
The CAB architecture allows these modules to interact with the shell's (and other modules) services and toolbars without having any dependency on any of the modules.
I've also designed the data providers to work independently - The data providers/modules only interact with a DataModel object. The module requests to retrieve SalesOrder record 50, and any datasources that inherit the ISalesOrder interface can respond to this request.
This will allow users to simply plug in a new data provider module for other accounting databases etc.
Extension Manager
I've just finished working on an extension manager for TMS. This allows users to download new modules for their TMS application - User's can basically build and customise their application to suit their needs.
It also checks for updates using a Web Service. The assembly version numbers are compared, and the user is asked if he/she wants to download the update.
Layout Problems
The whole idea behind TMS was that every module would be independent. The application has Layout modules that define the structure for the user interface. One of these layout modules in TMS creates the toolbars at the top of the window and exposes them as a UIExtension. Other modules can access UIExtensions and add/remove buttons.
The problem I am having I am creating new instances of this ButtonItem class and adding them to the toolbar. This means that if I ever create a new layout module, I cannot change the toolbar type because the modules are dependent on this toolbar type.
This is the problem I am trying to solve now.
I will post again later today