So after part 1, we have a shell ready to go. We haven’t done anything CABish yet, but we will in this post.
The first thing we need to do is create our layout that modules will be loaded into. If you remember, CAB Shells basically contain 2 possible things: Workspaces and UI Extension sites. It just so happens that we’ll need both!
First, we’ll need a menu for loading the image and applying filters. That will be our UI Extension Site.
Second we need a workspace in which to load the image display module.
Here I added a MenuStrip and created our default File menu. Give it a name like mainMenuToolStrip so that you can refer to it later.
Next we want to add a workspace so that we can load the image. To gain access to these CAB UI controls, you need to add them to the toolbox on the left. Right click on the Toolbox Pane, select Add Tab and name it CAB Controls. Then right click on the name and select Choose Items. You’ll have to Browse for the Microsoft.Practices.CompositeUI.WinForms.dll. Select all of the controls and click OK.
Now you should see a toolbox group like this:
We will use the DeckWorkspace control. Drag that onto the form and name it mainWorkspace. Make sure that you dock it to fill the form.
It should look like this:
We have our basic form, now it’s time to load the first module into it!
Create a new class library project called ImageDisplayModule. Rename Class1.cs to ImageDisplayModuleInit.cs. You’ll need a reference to the dlls mentioned above as well.
If you recall, a module contains work items. Our first work item is DisplayImageWorkItem. It’s responsibility is to load an image and display it on the form.
Let’s start out by creating the View. The view is a user control with a [SmartPart] attribute applied. The view is going to consist of an ImagePlaceHolder. Also keep in mind that we are doing this with Model-View-Presenter, so we’ll start by identifying the interface for our view.
[SmartPart]
Create an interface called IDisplayImageView. The only real member that this interface ensures is that you can set and retrieve an image.
public interface IDisplayImageView
{
public Bitmap Image
get;
set;
}
Next, create a new UserControl called DisplayImageView. This user control needs to implement the IDisplayImageView interface. Also notice that I have placed the [SmartPart] attribute on the class as well.
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI.SmartParts;
namespace ImageDisplayModule.cs.WorkItems
public partial class DisplayImageView : UserControl, IDisplayImageView
private Bitmap _image;
public DisplayImageView()
InitializeComponent();
#region IDisplayImageView Members
get { return _image; }
set { _image = value; }
#endregion
Next, we’ll switch over to design view and drag a PictureBox on the control. I’ve also added a dummy label so that we can identify this control on the form in a minute.
PictureBox
Here’s what it looks like:
I’ve also created a presenter that will control all of the UI logic for this view:
public class DisplayImagePresenter
private IDisplayImageView _view;
public DisplayImagePresenter(IDisplayImageView view)
_view = view;
[EventSubscription("topic://ImageEditor/ImageDisplayModule/ImageLoaded", Thread=ThreadOption.UserInterface)]
public void OnImageLoaded(object sender, DataEventArgs<Bitmap> e)
_view.Image = e.Data;
Here you can see that the view is accepted in the construtor, and we are responding to an event. The topic:// is just a hierarchical way of naming events that we might care about. Basically the presenter is going to listen for an event when a picture is loaded, and set the image to the view once it fires.
In order to load this module into our shell, we need a few more pieces. The first is a ProfileCatalog.xml file. This tells the shell what modules to load.
Place an xml file in the shell project and make sure that the properties are set to Copy Always so that it will end up in your output directory. The file should look like this:
<?xml version="1.0" encoding="utf-8" ?>
<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile">
<Modules>
<ModuleInfo AssemblyFile="ImageDisplayModule.dll" />
</Modules>
</SolutionProfile>
The important concept to grasp here is that there is no project level reference to the module project. The dll is placed directly in the shell’s bin folder, and CAB is going to read this file to know to load it.
When CAB goes to load a module, it will scan the assembly for a class that inherits from ModuleInit. So, I created an ImageDisplayModuleInit class :
ModuleInit
ImageDisplayModuleInit
public class ImageDisplayModuleInit : ModuleInit
private WorkItem _rootWorkItem;
[ServiceDependency]
public WorkItem RootWorkItem
get { return _rootWorkItem; }
set { _rootWorkItem = value; }
public override void Load()
base.Load();
//create workitem
DisplayImageWorkItem wi = RootWorkItem.WorkItems.AddNew<DisplayImageWorkItem>();
//run the workitem
wi.Run();
Here we take a reference to the Root WorkItem, which is provided to us through the [ServiceDependency] attribute from ObjectBuilder. The Load() method is called which instantiates our work item and runs it.
The work item then needs to load up any smartparts and place them in the required workspaces.
public class DisplayImageWorkItem : WorkItem
private DisplayImagePresenter _presenter;
protected override void OnRunStarted()
base.OnRunStarted();
ShowView();
private void ShowView()
//create the view
_view = this.SmartParts.AddNew<DisplayImageView>();
_presenter = new DisplayImagePresenter(_view);
this.Workspaces["mainWorkspace"].Show(_view);
This part is pretty self explanatory. We take the workspace name from the shell and tell it to show our smart part.
Everything is wired up now, so if you run the application you should see that the module has been loaded into the shell.
If you have troubles getting everything to work, make sure that your Module project is setup to output the dll inside the Shell’s bin folder, and also verify that the profile catalog xml file is placed there as well.
Next time we’ll figure out how to load the image using a different workitem.
I'm Ben Scheirman. I am a .NET software developer with a strong interest in agility. I work as a Principal Consultant with Sogeti.
Read more here.
email me
Ads by The Lounge
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.