Rich Newman

November 24, 2007

Workspace Types (Introduction to the CAB/SCSF Part 17)

Filed under: .net, c#, CAB, Composite Application Block, Workspace — richnewman @ 10:25 pm

Introduction

Part 16 of this series of articles explained in general terms why Workspaces are useful. It also examined the methods that are available on a Workspace via the IWorkspace interface.

There are five Workspace types provided with the Composite Application Block framework: DeckWorkspace, ZoneWorkspace, TabWorkspace, MdiWorkspace and WindowWorkspace. This article looks in a little more detail at these various Workspace types and associated SmartPartInfo types, and gives some code examples.

The ToolBox

Some Workspaces can be added to the Visual Studio toolbox as shown below. To do this you (as usual) right-click and select ‘Choose Items…’. Then click the ‘Browse…’ button, and browse to your Microsoft.Practices.CompositeUI.WinForms.dll library. If you click ‘OK’ to this the Workspaces shown should be added:

smartparttoolbox.jpg

DeckWorkspace

The DeckWorkspace is the first of the Workspace types provided with the CAB that we shall look at.

This is ‘deck’ as in ‘deck of cards’. When we show a SmartPart in a DeckWorkspace it fills the area of the Workspace completely. If we show another SmartPart it replaces the original SmartPart in the view completely. However, the old SmartPart is still there in the deck, immediately behind the original SmartPart. If we add a third SmartPart it gets displayed at the front of the deck, but the other two SmartParts are still there in order. If you close the third SmartPart the second one will get displayed.

A code example of use of a DeckWorkspace is available. The shell form for this CAB application has a DeckWorkspace on it. The project also contains two SmartParts, with red and blue backgrounds. A series of buttons allow the user to call methods on the DeckWorkspace, providing either the red or the blue SmartPart as an argument. The methods available are the IWorkspace methods discussed in part 16: Show, Hide, Activate, Close.

deckworkspace.jpg

This behaves as described in part 16. If we try to Activate, Close or Hide a SmartPart that hasn’t been Shown we get an exception. However if we Show the red SmartPart, then Show the blue SmartPart, then Activate the red SmartPart the red SmartPart that was originally shown gets shown again.

As shown above, the form also has a ‘Details’ button. This shows the current ActiveSmartPart, and the Items and Workspaces collections on the RootWorkItem. As a result it will show you which SmartParts are loaded at any given time.

To use a DeckWorkspace you can simply drag one onto a form and start coding. There is no specific SmartPartInfo class for the DeckWorkspace .

The DeckWorkspace is a useful type if you are building an Outlook-style interface. It can act as the main display area in the application, and gives us the behaviour we desire (which was outlined in part 16).

ZoneWorkspace

The ZoneWorkspace allows the user to define ‘zones’ or areas within the Workspace where SmartParts can be shown. As mentioned briefly in part 16 a SmartPartInfo object can be used to define which zone a SmartPart will be shown in .

The easiest way to set up a ZoneWorkspace is firstly to drag one onto your form or user control from the ToolBox. Then drag ordinary Windows Forms panels onto the ZoneWorkspace and position them to define your zones. If you look in their properties you will see that these panels have a ZoneName property where you should give your zones sensible names.

To show a SmartPart in a specific zone you need to tell it the zone via a SmartPartInfo object. The easiest way to do this is to drag a ZoneSmartPartInfo item from the Toolbox shown above onto your form or user control. You can make your ZoneSmartPartInfo reference a specific zone by setting ITS ZoneName property to the ZoneName of the zone. You can then show SmartParts in the zone by calling the Show method and passing the ZoneSmartPartInfo object as the second parameter.

A code example of this is available. Once again this has two SmartParts, red and blue. It also has two zones, left and right, as shown. Further it has two visual ZoneSmartPartInfo objects as discussed above, one of which references the left zone and one of which references the right zone. Which one of these is used when you click on the buttons is controlled by the radio buttons shown below.

The buttons are as above, apart from the Apply buttons. The Apply buttons call ApplySmartPartInfo on the ZoneWorkspace with the appropriate ZoneSmartPartInfo object depending on which of the radio buttons is selected. They can thus be used to change which zone a SmartPart is displayed in: if the red SmartPart is shown in the left zone (as shown below) we can check the ‘Right zone’ radio button and hit ‘Apply’ for the red SmartPart, which will move it to the right zone.

zoneworkspace.jpg

One thing to note about the ZoneWorkspace is that calling Show with a SmartPart does not necessarily bring that SmartPart to the front. The Show method adds the SmartPart to the appropriate collections, gives the SmartPart the focus and make the SmartPart into the ActiveSmartPart. However in spite of this the SmartPart can still be hiding behind another SmartPart.

For this reason DeckWorkspaces are often put into zones of ZoneWorkspaces, since a SmartPart will be brought to the front when Show is called on a DeckWorkspace.

TabWorkspace

TabWorkspace has the standard Windows Forms TabControl as a base class, and acts like a CAB version of that control. Every SmartPart that you add to the TabWorkspace gets shown in its own tab.

There’s a TabSmartPartInfo class we can use to set details on our SmartParts and tabs. This has a Title property which can be used to set the title of the TabPage the SmartPart is being displayed in. It also has a Position property. This can be set to TabPosition.Beginning or TabPosition.End, and affects where the new tab appears in relation to existing tabs.

There’s a code example that shows this, with the same buttons and red and blue SmartParts as in the previous examples. This has no tabs showing at start up. When we click the Show button for a SmartPart it calls the Show method for the TabWorkspace, passing the appropriate SmartPart as a parameter.

The Show method creates a new tab with the SmartPart displayed on it.

tabworkspace.jpg

In addition the example has visual TabSmartPartInfo components that are passed to the Show methods, one for the red SmartPart and one for the blue. These set the titles on the tab pages, and control where they are added.

This example also has ‘Apply’ buttons that let us change the titles of the tab pages from a value entered in a TextBox. The buttons do this by setting the Title property on the appropriate TabSmartPartInfo and then calling the ApplySmartPartInfo method.

The SmartPartInfo you give to the Show method implements ISmartPartInfo and has no additional members.

MdiWorkspace

The MdiWorkspace allows each SmartPart to be displayed in a separate child window inside a parent Form. This allows us to build MDI applications using the Composite Application Block.

One thing to note about the MdiWorkspace is that, as with other Workspaces, our child SmartParts have to be User Controls. We can’t display child Forms inside an MdiWorkspace with the code as it stands. This is in spite of the fact that our children look and behave like Forms: they have title bars and maximize and minimize buttons, for example.

MdiWorkspaces can use the WindowSmartPartInfo type to give additional information about SmartParts that are being displayed. As usual, this has a Title property. It also has properties to determine whether our MDI child is modal, whether it displays minimize, maximize and/or control boxes, and its location.

Once again a code example is available, and this behaves in the same way as the examples above. This includes allowing us to change the titles by entering some text in a TextBox and clicking ‘Apply’, which calls ApplySmartPartInfo as for the TabWorkspace above.

mdiworkspace.jpg

WindowWorkspace

A WindowWorkspace lets us display our SmartParts in floating windows, each one in a separate window. Once again it uses the WindowSmartPartInfo type to give additional information about SmartParts that are being displayed.

A code example is available. This is extremely similar to the MdiWorkspace example above, except that obviously it deals with a WindowWorkspace and not an MdiWorkspace.

windowworkspace.jpg

Conclusion

In this article we have reviewed the various types of Workspace that are available in the CAB framework, and have given some code examples.

So far in this series of articles we have only looked at the Composite Application Block. Part 18 of the series will rectify this by taking a first look at the Smart Client Software Factory.

November 16, 2007

More Detail on Workspaces and SmartParts (Introduction to the CAB/SCSF Part 16)

Filed under: .net, c#, CAB, Composite Application Block, SmartPart, User Interface Design, Workspace — richnewman @ 4:03 pm

Introduction

Part 15 of this series of articles explained SmartParts in the CAB, and gave a very brief introduction to Workspaces. This article will continue with the same topics. In particular we will look in more detail at Workspaces.

Workspaces Recap

Part 15 explained the SmartParts can be most easily thought of as user controls or views in a composite application block application.

Part 15 also explained that Workspaces are just controls that allow other controls (SmartParts) to be laid out within them, and differ from other container controls in that they interact with other objects in the CAB in a predefined way. In particular there is a Workspaces collection on any WorkItem, and they have a Show method that shows a SmartPart.

User Interface Design for Business Applications

In an earlier blog posting on user interface design I discussed some of the possible user interface designs for applications that have to display multiple different screens of data. A common design for these applications is to have a tabbed client area for the screens, together with a tree-based window manager of some kind. An alternative is an Outlook-style interface, again possibly with a tree window manager, but dispensing with tabs at the top of the screen. Clearly both of these designs are very similar: we click on a tab or double-click in a tree and our screen gets displayed in a predefined part of the screen.

One thing to notice with both of these designs is that after we have displayed a client screen for the first time we won’t usually close it again. When other screens are displayed our first screen will stay in memory. It will retain the state it was displaying the last time the user interacted with it. When the user requests to see the screen again it will just be brought to the front.

So we have a collection of objects (our screens) with persistent state whilst the application is running. We may create all of these screens at start-up. However more usually we’ll create a screen the first time the user requests it, and then redisplay it the second time it is requested (a sort of lazy initialization). Note that at any given time many of the screens in the collection may not actually be displayed at all.

Building User Interfaces for Business Applications

Of course it is easy enough to create an application of this kind using Windows Forms standard controls. However, there’s some infrastructure code that we are going to have to write. We need:

  • some means of caching our screens collection
  • a way of allowing new screens to be added to the screens collection
  • a method of displaying a screen when it is requested for the first time
  • a method of redisplaying the appropriate screen in response to the appropriate request

We’ll also want to make sure we control the scope of our screens carefully so that they can’t be accessed from anywhere in the code. As we all know, global singletons (or global variables of any kind) are a bad thing.

The good news is that Workspaces and SmartParts do all this for us. What’s more they do it in a very simple way.

Workspaces – the IWorkspace Interface

All Workspaces implement the IWorkspace interface. This is the interface we are meant to use to interact with our Workspaces: usually we don’t need any more functionality than this interface provides as we shall see.

The IWorkspace interface has a number of useful methods. In general these behave as explained below, although each type of Workspace is free to implement them in its own way:

void Show(object smartPart)

When this method is called, if the SmartPart passed as a parameter has not previously been shown in this Workspace it will be loaded and then activated. If it has already been shown and not been closed (i.e. if it is in the Workspace’s SmartParts collection) it will just be activated. Usually ‘activated’ means brought to the front and made into the ActiveSmartPart.

void Activate(object smartPart)

If the object is in the Workspace’s SmartParts collection it will be activated as described above. If it’s not in that collection then an ArgumentException will be thrown.

void Hide(object smartPart)

Hides the SmartPart (sends it to the back usually), but leaves it in the Workspace’s SmartParts collection. The next control in the Workspace will usually be activated (made into the ActiveSmartPart). What ‘next’ means here varies by Workspace.

void Close(object smartPart)

Hides the SmartPart and also removes it from the SmartParts collection. The next control in the Workspace will usually be activated.

IWorkspace also has a property ActiveSmartPart, which returns the active SmartPart if there is one. As discussed in part 15 Workspaces have a SmartParts collection, and the IWorkspace interface has a SmartParts property that lets us access this (it returns a ReadOnlyCollection<object>).

IWorkspace also has events SmartPartActivated and SmartPartClosing. You can probably guess when these get fired.

There are also some methods to deal with SmartPartInfo on IWorkspace. I’ll discuss SmartPartInfo below.

Workspaces – Aside on how the Supporting Classes Work

The core functionality of IWorkspace is encapsulated in a class called, appropriately, Workspace. This is an abstract base class that all Workspaces are expected to inherit from. It implements IWorkspace so the individual Workspace classes don’t have to (since they inherit the interface from Workspace).

The base class gives basic implementations of the members of IWorkspace, and also handles things like caching the active smart part, tracking the SmartParts collection, and raising the right events at the right time.
Whilst the way the supporting classes work is quite interesting, you don’t need to know it in any detail to use the Workspace classes. The exception to this is if you want to write your own custom Workspace class, in which case you will need to get to grips with them in more detail. Writing your own custom Workspaces is beyond the scope of these articles for now.

Further Aside on Multiple Inheritance in C#

The Workspace classes also demonstrate a method of achieving a kind of multiple inheritance in C#. This is done by composition, using a class called WorkspaceComposer and an interface called IComposableWorkspace. It’s needed because certain Workspace classes (e.g. TabWorkspace) already inherit a Control class to give their basic functionality (in the case of TabWorkspace this is TabControl). As we know, in .NET a class can’t directly inherit more than one other class.

It is not my intention in these articles to actually discuss the underlying code in the Composite Application Block in any detail. The aim of these articles is to let you use the CAB as a black box component. But the WorkspaceComposer is particularly interesting since it shows us a pattern for achieving multiple inheritance (albeit messily) in C#. If you are interested the code is in the Microsoft.Practice.CompositeUI.SmartParts namespace in the Microsoft.Practices.CompositeUI component.

SmartPartInfo

As we have seen the normal Show method on the IWorkspace interface only lets you pass the SmartPart you want to show as a parameter. There may be other information that the CAB framework will need to know to show your SmartPart correctly. For example, in a TabWorkspace you need to provide the text for a new tab somehow. In a ZoneWorkspace you need to tell the CAB which particular zone the SmartPart will be displayed in.

This problem is solved by passing an additional object to the Show method:

void Show(object smartPart, ISmartPartInfo smartPartInfo)

This additional object implements the interface ISmartPartInfo, which has Title and Description properties. It’s the Title property that’s used to set the tab page title in the TabWorkspace.

The smartPartInfo object can contain any additional information we require. For example, for ZoneWorkspaces it contains the name of the zone in which the SmartPart will be displayed.

The IWorkspace interface also has a method to directly apply the information in a SmartPartInfo object to a SmartPart that has already been displayed:

void ApplySmartPartInfo(object smartPart, ISmartPartInfo smartPartInfo)

In a ZoneWorkspace this can be used to change the zone where a SmartPart is shown in, for example. The next article in this series will give some examples of this.

Conclusion

This article has discussed some of the theory behind Workspaces, and shown the basic interfaces. Part 17 in this series of articles will discuss the various types of Workspace that are available in the CAB, and give some code examples of how to use them.

November 4, 2007

Composite Application Block or WPF Composite Client?

I’ve had a question from a Greg Bayer this week about what my thoughts were on the Composite Application Block and Smart Client Software Factory (CAB/SCSF) ‘now that it appears that both are being put out to pasture by Microsoft’. My response became a little long to be posted as a comment.

I suspect Greg was referring to the announcements from Microsoft about the ‘Windows Presentation Foundation(WPF) Composite Client’ and the death of Acropolis this week:

http://blogs.msdn.com/gblock/archive/2007/10/26/wpf-composite-client-guidance-it-s-coming.aspx
http://blogs.msdn.com/acropolis/archive/2007/10/29/An-Acropolis-Update.aspx

Acropolis, of course, was Microsoft’s ‘CAB/SCSF for WPF’ (we’re in acronym hell here). Put more simply, it was intended to be the next version of the Composite Application Block. The Acropolis team have released a few previews (CTPs), but now it appears that a new direction is being taken and something called the ‘WPF Composite Client’ will effectively replace it.

My thoughts on this are that it actually clarifies the situation for those of trying to write user interface software right now. No CAB replacement is arriving soon (a year away), so if we want a composite application framework from Microsoft the CAB has to be the one to go for. And there isn’t anything else on the market that is directly comparable, although obviously there are other dependency injection frameworks we could use.

Even when this CAB replacement arrives it is going to focus on Windows Presentation Foundation (WPF). WPF is the future for user interface developers, and we should all be looking at it. However, I wouldn’t use it for enterprise development right now. It’s still early days for a new technology, it has some serious failings currently (the user interface editor isn’t great), and it has a huge learning curve. It’s not at all clear that taking the risk of using such a new technology is going to give you sufficient business benefit to warrant the extra cost. Some of the major benefits of WPF just aren’t relevant for the sort of application I develop (e.g. better multimedia integration).

And what’s more, you can’t run it on Windows 2000, which can still be an issue if you work in a major organization.

As Glenn Block says ‘Win Forms is not dead.’ ‘Win Forms is the recommended breadth solution for LOB application development for the foreseeable future’.

So, in answer to Greg’s question, the CAB is still relevant, and I would still use it on an application I was starting to build today. That answer may well change in a year’s time however.

Having said all that, my opinions on how we should use the current version of the CAB may not be what you expect. The CAB is a superb piece of software. Even where the Patterns and Practices group have got things wrong they’ve done so in an interesting way, and you can learn from their mistakes.

However using an all-pervasive dependency injection framework can get you into a scoping mess. By ‘all-pervasive’ I mean putting every object you create into a dependency injection container, and only mapping dependencies through those containers (never using ‘new’). Controlling scope doing this is difficult, and you can end up destroying all the encapsulation advantages of object-orientation. I’ll be blogging about this in the future, but I think a more restricted use of the CAB may be appropriate.

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 80 other followers