Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dockable CAB Workspaces

0.00/5 (No votes)
11 Mar 2006 1  
Custom Workspaces for the CompositeUI Application Block using DivElements SandDock controls

Prerequisites

The code covered in this article uses a proprietary control suite from DivElements. You must purchase a license to use the DivElements product or download a 30-day evaluation copy to run the code. You can download SandDock at http://www.divelements.com/.

Introduction

The CompositeUI Application Block (CAB) is the latest offering from Microsoft to aid developers in creating extensible, modular, pluggable applications. If you’re not familiar with CAB, I strongly suggest you stop reading this article and cruise over to MSDN and read through some of the documentation available there. There are also a number of hands on labs that will help you understand what the CAB is all about.

There are a lot of features offered by this application block. I have spent the last three weeks looking over it and am just barely beginning to put the pieces together. The portion of the block covered in this article is Workspaces. The graphics below show my DockableWindowWorkspace and TabbedDocumentWorkspace in action.

[Docking Hints]

[Side By Side]

Workspaces for Dummies

I don't claim to fully understand the concepts behind the CompositeUI Application Block, but here is my sixty second summary of what Workspaces are and how you use them. The Workspace acts as a container for the SmartPart views exposed by your WorkItems. The CAB comes with several built in Workspaces for showing SmartParts in Windows, TabControls, Zones, and Decks. The WindowWorkspace shows each SmartPart in its own Window. The TabWorkspace shows each SmartPart in its own TabPage. If you are wondering what a SmartPart and WorkItem are, that is a pretty good sign you need to go through the CAB hands on labs before reading the rest of this article.

The design of the application block allows a developer to change a Workspace without breaking the application. This leads to the concept of a "shell" application. The shell application provides one or more Workspaces and allows dynamically loaded modules to display WorkItems in those Workspaces. The shell application can be modified to completely change the way a user interacts with WorkItems. I will be demonstrating this concept by modifying one of the hands on lab samples to use my custom Workspaces.

Microsoft has made it fairly painless and straight forward to create custom Workspaces. One of the hands on labs walks you through creating a custom Workspace using a TreeView control. My custom workspaces were created based on the WindowWorkspace included in the CAB source download.

Workspace Design

Both the DockableWindowWorkspace and the TabbedDocumentWorkspace must be constructed with an existing SandDockManager. Those of you who have used the SandDock controls from DivElements will be familiar with the SandDockManager. The SandDock controls allow you to create an interface similar to that provided by Visual Studio, complete with pinnable/collapsible windows that can be rearranged by the user. See the screen shots below for an example of such a UI.

[Sidebar on Left]

[Sidebar Collapsed]

The SandDockManager is a component that must be added to your shell application form. The code below from BankShellForm.cs shows the creation of the custom Workspaces, passing the SandDockManager previously added to the form.

[InjectionConstructor]
public BankShellForm(WorkItem workItem, 
                     IWorkItemTypeCatalogService workItemTypeCatalog)
  : this()
{
  this.workItem = workItem;
  this.workItemTypeCatalog = workItemTypeCatalog;

  /// ADDED
  this.sideBarWorkspace = new DockableWindowWorkspace(this.sandDockManager);
  this.contentWorkspace = new TabbedDocumentWorkspace(this.sandDockManager);
  this.workItem.Workspaces.Add(sideBarWorkspace, "sideBarWorkspace");
  this.workItem.Workspaces.Add(contentWorkspace, "contentWorkspace");
  ///
}

Internally, the SandDockManager does most of the work for us; the custom Workspaces are just wrappers around the SandDockManager that implement the IWorkspace interface.

IWorkspace exposes methods for showing, hiding, activating, and closing SmartParts. The custom Workspaces simply show, hide, activate, and close either a DockableWindow or a TabbedDocument associated with the SmartPart.

Persisting Layouts

The SandDock controls support the concept of persisting their layout between instances of the application. This means that if your savvy users decide to take time rearranging their Workspace windows and tabs to their liking, the SandDockManager will create an XML file that can be saved to the users' disk and loaded the next time they launch the application. The code below from BankShell.cs is a quick and dirty implementation that saves/loads the SandDock layout to a file called SandDockLayout.txt located in the same directory as the application executable.

/// ADDED
private string _ReadLayout()
{
  using (FileStream fs = System.IO.File.Open("SandDockLayout.txt",
    FileMode.OpenOrCreate, FileAccess.Read))
  {
    using (StreamReader sr = new StreamReader(fs))
    {
      return sr.ReadToEnd();
    }
  }
}

private void _WriteLayout(string layout)
{
  using( FileStream fs = System.IO.File.Open("SandDockLayout.txt",
    FileMode.Create, FileAccess.Write))
  {
    using (StreamWriter sw = new StreamWriter(fs))
    {
      sw.Write(layout);
    }
  }
}

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
  base.OnClosing(e);
  if (!e.Cancel)
  {
    _WriteLayout(this.sandDockManager.GetLayout());
  }
}
///

protected override void OnLoad(EventArgs e)
{
  //. . .

  /// ADDED
  string layout = _ReadLayout();
  if (!String.IsNullOrEmpty(layout))
  {
    try
    {
      this.sandDockManager.SetLayout(layout);
    }
    catch { }
  }
  ///
}

This is a great feature to take advantage of but may prove difficult for WorkItems that are loaded dynamically. To help you implement this feature in your applications, I created the IDockableSmartPart interface that you can use to mark your SmartParts that will persists their layout.

IDockableSmartPart

Implementers simply define a Guid for their SmartPart that will be used by the SandDockManager to identify the location and layout of the SmartPart between uses of the applications. See the example code below from SideBarView.cs:

public partial class SideBarView : UserControl, IDockableSmartPart
{
  private static readonly Guid dspGuid = 
    new Guid("{B69E82F9-5FA2-4309-8FC0-0F421D12F0EB}");
  
  //. . .
  
  Guid IDockableSmartPart.Guid
  {
    get { return dspGuid; }
  }
}

You can test the BankTeller application by running the application, moving the SideBarView to the right side of the screen, close the application, then re-open the application. The SideBarView should be displayed on the right side of the screen now. Wherever you put the SideBarView, it should be remembered and reloaded in the correct location each time.

[Sidebar on Left]

[Sidebar on Right]

You can use the built in State and IStatePersistanceProviders for your WorkItems to remember their Guids between uses, and pass those Guids to your IDockableSmartParts so they will be loaded and arranged properly by the custom Workspaces.

Unit Testing

I pilfered the unit tests for the TabWorkspace and WindowWorkspace that come with the CompositeUI Application Block source code and modified them to suit my needs. If you have nUnit installed on your machine, you can run the GUI interface on the WCPierce.Practices.CompositeUI.WinForms.Test assembly and see all of the pretty green lights. I added very little to the existing tests so I would say the DockableWindowWorkspace and TabbedDocumentWorkspace have about 90% of their features covered by the unit tests.

(UPDATED: alexsantos posted the code to correct this problem. Use the download link above for the new code). One problem I ran into with the TabbedDocumentWorkspace was properly setting focus to sibling tabs when an existing tab was closed. Those of you with greater experience using the SandDock controls may be able to solve this. If anyone comes up with a valid solution, please let me know and I will incorporate it into the Workspaces.

Conclusion

I do want to make it perfectly clear that the code download does not include a dockable/pinnable windowing framework. That amazing functionality is provided by the SandDock controls from DivElements. The code download does include a nice little wrapper that, when coupled with the amazing work done by the Microsoft Patterns & Practices team, will allow you to create highly dynamic and customizable applications in a modular fashion.

I greatly appreciate comments, ratings, feedback, etc. So if you found this article helpful, or you think it stinks, please let me know via the comments section below.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here