Friday, March 09, 2007

Writing a Simple Image Editor in CAB - Part 2

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.

Our first UI Extension Site

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:

CropperCapture[43]

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:

CropperCapture[44]

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.

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

{

    [SmartPart]

    public partial class DisplayImageView : UserControl, IDisplayImageView

    {

        private Bitmap _image;

 

        public DisplayImageView()

        {

            InitializeComponent();

        }

 

        #region IDisplayImageView Members

 

        public Bitmap Image

        {

            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.

Here’s what it looks like:

CropperCapture[45]

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 :

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 IDisplayImageView _view;

    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.

the app running with a module loaded

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.

Writing a Simple Image Editor in CAB - Part 1

I promised posts with more “meat,” so here it goes.  I’m going to demonstrate how to start writing a CAB application by building a simple image manipulation program.

Overview:
The application will load an image from some flexible source (file system, flickr, whatever).  The image will be displayed on the canvas.  The user will have various filters to apply to the image.  These filters should be easily added later on, and they should all manipulate the image in some way.  When everything is done, the user will have the option of saving the changes to disk.

So now that we have our high-level description of the app, let’s build it!

Start up Visual Studio 2005, create a blank solution called CAB-ImageEditor.  Add a new C# Windows application called ImageEditorShell.  This will be the basic structure of the application, and we will add things to it.

Add the following references to your project:

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

First you need to delete the files that Visual Studio gives you out of the box.  We will start from scratch.

Create a new Form called ImageEditorShell.cs.  Give it a window title if you like.

Our blank shell

Create a new class called ImageEditorApplication.cs.  This will be the entry point for our application.  We need to inherit the class from FormShellApplication<T,K> and give it the type of the root work item and our shell.

using System;

using Microsoft.Practices.CompositeUI;

using Microsoft.Practices.CompositeUI.WinForms;

 

namespace ImageEditorShell

{

    public class ImageEditorApplication : FormShellApplication<WorkItem, ImageEditorShell>

    {

    }

}

We need an entry point to start the application, so add that…

public class ImageEditorApplication : FormShellApplication<WorkItem, ImageEditorShell>

{

    [STAThread]

    public static void Main()

    {

        new ImageEditorApplication().Run();

    }       

}

That’s all we need to get our application running.  Go ahead and push F5 and see what happens.

Man, are you impressed yet?  Hehe, chill out.  There’s more to come…

CAB - Hands on Labs

The very best resource I have found so far about working with CAB are the Hands On Labs.

For some reason they were very difficult to find for me.

If you’re looking to understand CAB from a very beginner’s perspective, then I highly recommend you go through them.  There are 9 in total.

Thursday, March 08, 2007

So What's a WorkItem?

Probably the largest single piece that you need to understand about CAB is the WorkItem.

As I mentioned before, WorkItems are usually 1:1 with use cases.  For example, say we are buiding a CRM product, we might have a use case called “Edit Customer.”  This would map to an EditCustomerWorkItem.

Think of the WorkItem as a container for anything related to the use case.  It can house various elements such as UI components, state, services, etc.  This gives you a nice boundary for when things get instantiated or destructed.

WorkItems live inside of modules, and are instantiated by the ModuleInit class.

So say, given the example above, we have loaded our CustomerInformationModule, which contains an EditCustomerWorkItem.  The workitem might contain:

  • A reference to a customer object, or customer id
  • A service used to retrieve/save the customer
  • A controller for implementing UI logic and interacting with our customer object
  • A view (control) that displays the various controls necessary to complete the use case.  It might have some textboxes, a Save button and a Delete button.

If you don’t do your use case homework, your work items will “go against the grain” of the application and add unnecessary complexity to the whole framework.  Do your use case work up front, and you’ll be designing better WorkItems.

Wednesday, March 07, 2007

What's in a CAB Module?

One of the root components of a CAB Application is the module.  So what is a module, exactly?

A Module is basically a collection of WorkItems.  Since WorkItems are generally 1:1 with use cases, then modules are a package for related use cases.

Modules

Modules get loaded by the application Shell, via the ProfileCatalog.xml file.  When you are structuring your solution, you do not include a project reference to the module project in your shell project.

One benefit of doing this is that you can create new modules and load them into a shell without recompiling the main shell solutions.  You could easily package up a patch which included a new ProfileCatalog.xml and a dll as a zip file.

Modules also help you organize your WorkItems into logical units so that they become more managable.

 

Digging into CAB - The Terminology

The Composite UI Application Block (or CAB for short) has a lot of terminology to understand.  In order to follow the downloadable samples you should first brush up on what the various components of CAB are.

Shell
The Shell is basically your application.  The Shell’s responsibility is to load and initialize UI services, modules, and other CAB components.  The Shell itself won’t contain many direct UI elements (these will be added later).  The Shell will typically house UIExtensionSites and Workspaces.

Module
Modules are basically packages of WorkItems, which we will see next.  Modules are defined in the ProfileCatalog.xml file so CAB can load them and they have constructs for initializing.  Each module resides it its own assembly.

WorkItem
Your use cases will generally become WorkItems.  A WorkItem is a container for UI elements, model classes, state, and services that are required to satisfy the use-case.  If you were using NHibernate, this would be an ideal place to demarcate your ISession boundary.

Workspace
Workspaces are the containers on your UI that SmartParts (User Controls) can be loaded.  Examples include DeckWorkspace, TabWorkspace, etc.  Workspaces can be moved around and replaced at runtime easily.

UI Extension Sites
UI Extension Sites are static UI elements, such as Menus and Toolstrips, and as such are added directly to the shell.  Different work items can access these to modify their contents.  A good example is setting status text in a status bar at the bottom of the application shell or adding menu items depending on what tab has focus.

SmartParts
SmartParts are your basic user controls, only denoted with a [SmartPart] attribute.  Think of these as the “View” in Model-View-Controller.  As such, these will contain controls, however all logic will be deferred to a controller or presenter class.

Service
As you might expect, Services are very vague.  They can be any type of service that provides your UI with logic or data.  In a CAB architecture, you will abstract most of the data and calculations to services that are consumed by the UI.  Out of the box CAB comes with a Catalog service for loading modules, a State service for persisting WorkItem state to a file or isolated storage, and an authorization service.

 

I think that gives us a good overview of the main terms and responsibilities.  Until next time…

Tuesday, March 06, 2007

Getting Started With CAB

I’m starting to scratch the massive surface that is CAB.  CAB is better known as the Composite UI Application Block developed by the Patterns & Practices Group at Microsoft.

It’s hard to define exactly what CAB gives you in one sentence, but I’ll try.  How about manageable windows forms architecture with an emphasis on patterns ?  I’m an ASP.NET guy, so that’s where my expertise lies, however I have built a few moderately sized WinForms apps and they get unwieldy very quickly.

I plan on posting a bit about CAB as I dig into it, so stay tuned for that.  For now I’ll just post my first experience with it:  Getting the darn thing installed.  In tune with many CTP and early release software, it doesn’t always go as smoothly as planned.

First thing to do is install the following things in the correct order:
(I assume you already have .NET 2.0 and Visual Studio 2005 installed)

I found some sources on the internet that listed those in reverse, and I got an error at the Guidance Automation Toolkit installation that said “Operation invalid for the current state of the object.”

In my case, I got the error even after uninstalling them all, but I was able to fix the error by modifying the registry.  Open regedit and navigate to HKEY LOCAL MACHINE\SOFTWARE\MICROSOFT\COMMAND PROCESSOR\.  There should be a key in there called AutoRun.  I renamed this to AutoRunOld and the installation worked.

(I have a feeling that this setting was modified by an XPize installation, since they modify your command prompt settings… but I haven’t confirmed this.)

Now that everything is installed and running, check out these resources to get your feet wet with CAB:

The next post will definitely contain more “meat.”

Monday, March 05, 2007

Sark acquired by Sogeti

Many of you have already heard the news, but my company, Software Architects, was recently acquired by Sogeti.

My only real prior knowledge of Sogeti was that I knew that Tim Rayburn worked at the Dallas office.

It’s hard to know what to expect at first.  Most seem to think it’s an exciting change, probably because we will be introduced to a lot of new clients and have one of the largest Microsoft consultancies in Houston (among other cities).  I sincerely hope that the culture that I love here will not be disturbed.  Sark stands out among a number of similar companies here in Houston, largely because they have fun company meetings (open bar!), a great training program, you’re paid to get certifications, they have a great library and will order books if you need them.  We’re reimbursed for tolls and parking as well.

It is my hope that these things do not fall through the cracks as we make the transition to becoming “Sogetians” (ahh… just doesn’t have the same ring to it!).

We’re ramping up to integrate everything this month, mostly moving Sarks over to the Sogeti infrastructure.  March will definitely be an interesting month, that’s for sure.

Tags:

That's Just Plain Unprofessional

Codeplex ASP.NET error page

They should be ashamed of themselves…

Tags:
Friday, March 02, 2007

BlogJet - Version 2 Rocks!

I just upgraded to BlogJet version 2, and I wish I had done it sooner!  I always found it annoying that I couldn’t add tags on the fly through BlogJet, however now I can!

The interface looks a lot cleaner as well.  I’m sure I will uncover some other cool new features as I use it, but for now… if you’re not writing to your blog with BlogJet, go try it!

Texter - Transform any textbox into a snippet-aware editor

This is seriously cool.

Lifehacker has a couple video tutorials that only last about 30 seconds that will explain it all.  Basically, think of it like turning your textbox into a mini TextMate, this works for Notepad, BlogJet, or just about any old web input box.

http://lifehacker.com/software/texter/lifehacker-code-texter-windows-238306.php

Wednesday, February 28, 2007

JavaScript memory leaks

Most people don’t realize this, but JavaScript can leak memory if you don’t write it properly.  This is a strange concept, because for one, it runs in the browser, two, it’s a scripting language so it feels simpler, and 3 because you usually don’t do very much in JavaScript.

But, when JavaScript is written carelessly, it can lead to enourmous memory leaks that can actually crash the browser.

Take a look at this incredible article on javascript memory leakage up at CodeProject posted by Volkan Ocelik to find out the different leakage patterns and how to avoid them. In order to fully understand how to avoid memory leakage, it is important that you understant closures. The best explanation I have found is here. So make sure you think when writing JavaScript so you don't hurt yourself.

On to a new client

I wrapped up my project at a client today, so I’m on to a new one on Monday.

The project consisted mainly of SharePoint, but I did get to do some interesting development aside from that.  I worked on a Stock Quote web service that would gather information from pluggable sources.  They wanted to pay for a dedicated source, however politics and paperwork were such a barrier I ended up just scraping Google finance.  It won’t last forever, but it’s free and avoids the paperwork.

We wrote a few custom SharePoint Web Parts, and I found out first hand what a pain in the ass it is to deploy these things.  The deployment project outputs a CAB file.  This CAB file needs to be copied onto the server, and you have to use the STSADM tool to install it into the portal server.  This means you need Remote Desktop access just to deploy to a server!  Our production servers are load-balanced, so this means you have to do it twice.  To save us a lot of time and headaches I wrote a Windows service that would monitor a CAB file for changes using a FileSystemWatcher, when it detected a change, it would…

  • Extract the CAB to get the dlls
  • Copy the dlls to the target bin folder
  • Optionally sign the assembly so it can be installed into the GAC
  • Run STSADM to remove the old CAB file from the portal server
  • Run STSADM to get the new CAB file in the portal server
  • Reset IIS

This thing worked like a charm!  We could kick off a build, run a batch file the copy the new CAB over, and the thing would install itself.  I wish I could release the code here, but I wrote it at a client site, so it belongs to them.

We also managed to contort a SharePoint Portal site into something actually pleasing to look at, and ripped out most of SharePoints crappy markup.  Why in the world does SharePoint output so many damn tables?

To be clear, this is all with SharePoint 2003, so I imagine MOSS 2007 is loads better, but I haven’t yet worked on it.  Anyway, my next project doesn’t involve SharePoint, so I’m happy.

Wednesday, January 31, 2007

MCMS Installation Woes

Installing MCMS 2002 SP1a on my laptop proved quite difficult this week.  This is one product that Microsoft really did not focus on the install experience.

I first tried to install it without Visual Studio 2003.  It complained, so I got Visual Studio Installed.  It also asked for the IE Web Controls, so I downloaded those, extracted it, and ran the build.bat file.  I copied the sample app as directed and everything was working there.

I didn’t have SQL Server installed locally, so I didn’t have SQL-DMO, but the installer didn’t tell me how to get it.  It kept complaining that I didn’t have Visual Studio 2002, Visual Studio 2003, IE Web Controls, and SQL-DMO, when I clearly had 2 of those 4.

I installed the SQL Client tools and that solved the SQL-DMO requirement.  Then I found an MSI installer for IE Web Controls, and that did the trick there (apparently it doesn’t REALLY check to see if you have it, it just checks the Add/Remove Programs section).  Visual Studio 2002 isn’t really required, the wording is just off on the installer.

That was a waste of a day.  I could have gotten much more accomplished if this installation had been easier.

Loans - Credit Card - Mortgages - Credit Counseling