Rich Newman

October 27, 2007

Introduction to SmartParts and Workspaces (Introduction to CAB/SCSF Part 15)

Introduction

Part 14 of this series of articles discussed UIExtensionSites. This article continues our discussion of user interface elements in the Composite Application Block by giving an introduction to SmartParts, and a very brief introduction to Workspaces. Part 16 will expand on this discussion. Workspaces and SmartParts are interlinked and to understand one you need some understanding of the other.

Why do we need Workspaces and SmartParts?

We don’t have to use any of the new user interface elements described in this article. We can create a composite application as described in previous articles using just the existing Form class, UserControl class and the various visual components found in the standard toolbox in Visual Studio. Part 1 and part 5 of this series of articles showed how we can construct a very basic multiple document interface (MDI) application comprising three independent modules. Those articles didn’t use any of the CAB user interface components.

Many introductory texts on the Composite Application block start with Workspaces and SmartParts. This series of articles has left them until close to the end. This is because I personally found SmartParts very confusing (and still do to a certain extent), and because we don’t need to use them to get the benefits of the Composite Application Block. Workspaces are more useful, as we shall see.

SmartParts

‘SmartParts’ were first introduced in the original CAB code back in 2005. As mentioned above the concept can be confusing. SmartParts are most easily thought of as user controls in a CAB application. In fact they can be any visual element low-level element in your CAB user interface. They can, for example, be child forms in an MDI application.

However, in almost every case you will come across SmartParts will be user controls (including the normal children in CAB MDI applications).

Microsoft’s CAB documentation defines a SmartPart as ‘a view of data (in the MVC pattern) such as a control, a Windows Form, or a wizard page’. This is slightly misleading as SmartParts don’t necessarily have to display any data, and don’t have to be in an MVC pattern.

SmartParts Collections

In part 2 of this series of articles we saw that a WorkItem can be thought of as a ‘run-time container of components’ and that the WorkItem class has various collection classes associated with it (e.g. Items, Services, WorkItems). One of these collection classes is the SmartParts collection. As we shall see later in this article, another collection class on a WorkItem is the Workspaces collection.

So perhaps a better definition of a SmartPart is as any object that is put into the SmartParts collection in a WorkItem in the CAB.

However, even this definition is confused by the fact that the Workspaces collection itself has a SmartParts collection. This means there are two SmartParts collections at different places in any WorkItem. This article will call these ‘WorkItem SmartParts’ and ‘Workspace SmartParts’. The two collections behave in different ways, which are discussed below. Before that we’ll take a quick look at what a Workspace actually is. This brief exposition will be expanded on in part 16.

Workspaces

Microsoft’s CAB documentation defines Workspaces as:

“The components that encapsulate a particular visual layout of controls and SmartParts, such as within tabbed pages.”

Workspaces are themselves controls that allow other controls to be laid out within them. In particular they are designed to allow SmartParts to be laid out within them.

Several Workspace types are available in the Visual Studio toolbox and can just be dragged onto our screens. In this sense they are like the existing layout controls in the .NET Framework (e.g. the TabControl).

Where Workspaces differ from existing layout controls is in the fact that they are part of and utilize the CAB dependency injection containers (Workitems). As with SmartParts, WorkItems have a ‘Workspaces’ collection which, obviously, contains all the Workspaces associated with the WorkItem.

We can create a Workspace in our Workspaces collection in the usual way with the AddNew keyword. However, we don’t need to do this if we’ve just dragged our Workspace onto a screen: the ObjectBuilder will recognize it as a Workspace when it is created, and add it to the appropriate collections.

Workspaces also interact with SmartParts in a standard way: they have a Show method that will display a SmartPart, an ActiveSmartPart property that gets a reference to the active SmartPart in the Workspace, and so on. These methods will be examined in more detail in part 16 of this series of articles.

SmartParts Collections: WorkItem SmartParts

We look now at the SmartParts collections in more detail. To show the concepts involved there is a code example available.

The SmartParts collection in a WorkItem is just a filter on the Items collection, filtering for any Items where the underlying class is decorated with the attribute ‘SmartPart’. If we have an object whose class is decorated with the SmartPart attribute and add it directly to either the Items collection or the SmartParts collection it will appear in both. However, if we try adding an object NOT decorated with the attribute to the SmartParts collection it will get added to the Items collection only.

The underlying type of our SmartPart doesn’t matter for this . It can be any class at all. As long as that class is decorated with ‘SmartPart’ it will behave as described above. The intention is that objects added to this collection be visual ones (user controls, forms etc.) but they don’t have to be.

Example of WorkItem SmartParts

This is illustrated in the code example. Here we have a user control, UserControl1, that is decorated with the SmartPart attribute:

    [SmartPart]
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }
    }

In the AfterShellCreated method we add this to the SmartParts collection of the RootWorkItem:

RootWorkItem.SmartParts.AddNew<UserControl1>();

We then examine what effect that has on the various collection classes on the RootWorkItem. We can see that the UserControl1 object is in both the SmartParts collection and the Items collection as we would expect (see below for the results output).

Similarly we define a plain old .NET class and also decorate that with the SmartPart attribute:

    [SmartPart]
    internal class TestClass {}

We then just add that to the Items collection of the RootWorkItem in AfterShellCreated:

RootWorkItem.Items.AddNew<TestClass>();

If we now look at the RootWorkItem collection classes we see that the TestClass object is in both the SmartParts collection and the Items collection in exactly the same way as the UserControl instance above. It is a ‘SmartPart’ even though it has no visual element.

The full output of the relevant collections from this example is shown below under ‘Output from SmartParts Example’.

SmartParts Collections: Workspace SmartParts

As discussed above, the second SmartParts collection is on a Workspace. An object gets added to this collection if we call the Show method on the Workspace with the object as the parameter.

For the Workspace SmartParts collection it doesn’t matter whether the object’s class is decorated with the ‘SmartPart’ attribute or not: it gets added to the Workspace SmartParts collection when the Show method of the Workspace is called regardless.

Note that the Show method doesn’t add the object into the Items collection of the WorkItem, nor does it add the object into the WorkItem SmartParts collection.

We’ll examine how Workspaces work, and how they interact with SmartParts, in more detail in part 16 of this series of articles. In particular we’ll look at why the Show method is a useful one, and we’ll examine how SmartPartInfo classes work and why we need them. For now you can just accept that this second collection exists and can be useful.

Issues with Workspace SmartParts

One issue here is that objects can only be added to the SmartParts collection of a Workspace if they inherit System.Windows.Forms.Control at some level. That is, in this case our SmartParts are forced to be visual controls.

Also you can’t add SmartParts directly to the SmartParts collection of a Workspace. The only way to add an object to this collection is to use the Show method of the Workspace (I think). Note that they don’t get removed from this collection just because you call Show with a different SmartPart.

You were warned that this is confusing.

Example of Workspace SmartParts

The code example discussed above has been extended to show how Workspace SmartParts work. We have added a DeckWorkspace (one of the more common Workspace classes) to our Shell form simply by dragging it from the Toolbox. We’ve also added another user control class to the project, UserControl2.

We then display the user control in the Workspace by calling the Show method:

this.Shell.deckWorkspace1.Show(new UserControl2());

This has the effect of adding UserControl2 to the SmartParts collection of the Workspace. However, UserControl2 is NOT added to the Items collection of the WorkItem, nor to the SmartParts collection of the WorkItem.

In the example above we defined a plain old .NET class and added it to the WorkItem’s SmartParts collection. We can’t do this with the Workspace’s SmartParts collection because this collection will only accept objects that inherit from Control at some level.

One thing to note here is that UserControl2 is decorated with the SmartPart attribute, but this makes no difference at all to the example. We can remove it and the example will work in the same way. This is because this attribute is not relevant for the SmartParts collection on a Workspace.

Output from SmartParts Example

The SmartParts example uses some simple looping code to output the collections created to the Output window. The results of this are shown below. As we can see, UserControl1 and TestClass (discussed in the section ‘Example of WorkItem SmartParts’ above) appear in the Items collection, and in the SmartParts collection at RootWorkItem level. However, UserControl2 (from the section ‘Example of Workspace SmartParts’) appears ONLY in the SmartParts collection on the Workspace. This is what we’d expect as discussed above.

ITEMS:
[d506f65b-e818-4379-b15a-5e53bfe7777f, Microsoft.Practices.CompositeUI.State]
[f4d17dcd-13cc-4d61-b24b-0b3cd03c88a8, Shell.Form1, Text: Form1]
[deckWorkspace1, Microsoft.Practices.CompositeUI.WinForms.DeckWorkspace]
[1d141251-3436-4a8a-8bd3-1e63ceca3d9e, Shell.UserControl1]
[632482f5-aaa3-490b-b800-3575bea33a06, Shell.TestClass]
SMARTPARTS:
[1d141251-3436-4a8a-8bd3-1e63ceca3d9e, Shell.UserControl1]
[632482f5-aaa3-490b-b800-3575bea33a06, Shell.TestClass]
WORKSPACES:
[deckWorkspace1, Microsoft.Practices.CompositeUI.WinForms.DeckWorkspace]
       SMARTPARTS:
       Shell.UserControl2

The SmartPart Attribute

Another source of confusion with SmartParts is what exactly the SmartPart attribute does. Several texts mention that it doesn’t seem to make much difference whether it’s there or not, and this is true to an extent with the Workspace SmartParts collection.

In fact there are (at least) two places where the SmartPart attribute makes a difference:

  1. As we’ve already seen any object decorated with the SmartPart attribute that is added to either the Items or the SmartParts collection of a WorkItem will effectively be added to both collections. Any object without the SmartPart attribute added to either collection will end up in the Items collection only. Remember that the SmartParts collection of a WorkItem is really just a filter on the Items collection for any objects that have the SmartPart attribute.
  2. The SmartPart attribute also makes a difference if we add a Control to the Controls collection of the Shell, or to the Controls collection of a Control on the Shell, when the Shell is being initialized (in its constructor). In this case the ObjectBuilder recognizes that we have a WorkItem SmartPart. It adds the SmartPart to the Items collection of the WorkItem and hence to the SmartParts collection (since it has the SmartPart attribute).

This is still confusing, but the behaviour in 2 is there so that if you drag a User Control with the SmartPart attribute onto the Shell from the Toolbox then it will get added to the SmartParts collection of the root WorkItem at start up.

Example of Effects of SmartPart Attribute on User Controls Dragged on to the Shell Form

Some example code showing this is available. This has a user control with the SmartPart attribute applied (SmartPartUserControl) and a user control with no SmartPart attribute (NormalUserControl). Both of these have been dragged onto the Shell form (Form1).

Additionally in the constructor of the Shell form we manually instantiate a SmartPartUserControl and add it to the Form’s Controls collection:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            SmartPartUserControl smartPartUserControl = new SmartPartUserControl();
            smartPartUserControl.Name = "Manually instantiated user control";
            this.Controls.Add(smartPartUserControl);
        }
    }

That’s all the code that’s been added. Once again the code dumps the various relevant WorkItem collections in AfterShellCreated by using simple loops outputting to the Output window:

ITEMS:
[fa70dbe9-0c30-443e-9fe6-a13e353994e0, Microsoft.Practices.CompositeUI.State]
[d67d7177-cf63-4b4f-9447-ef2fd1e8271b, Shell.Form1, Text: Form1]
[userControl11, Shell.SmartPartUserControl]
[deckWorkspace1, Microsoft.Practices.CompositeUI.WinForms.DeckWorkspace]
[Manually instantiated user control, Shell.SmartPartUserControl]
SMARTPARTS:
[userControl11, Shell.SmartPartUserControl]
[Manually instantiated user control, Shell.SmartPartUserControl]
WORKSPACES:
[deckWorkspace1, Microsoft.Practices.CompositeUI.WinForms.DeckWorkspace]
       SMARTPARTS:

As we can see, both SmartPartUserControls have been added to both the Items and WorkItem SmartParts collections, whilst the NormalUserControl does not appear (although we can see it on the Shell fine: it just hasn’t been added to these collections). This is what we’d expect from the discussions above.

Summary of the SmartParts Collections and their Intention

The intention of the Workspace SmartParts is to give us (and the CAB Framework) a collection of SmartParts that have been shown in a Workspace but not closed. These can be re-activated (effectively brought to the front). We can show Controls that are not marked with the SmartPart attribute in a Workspace (and hence arguably aren’t really SmartParts). However this collection needs to track everything that has been shown, and so it all ends up in this collection.

The intention of the WorkItem SmartParts is to give us a collection of SmartParts that we as developers can control and use. Here we do want everything in the collection to have the SmartPart attribute: this is what distinguishes SmartParts from ordinary Items as far as this collection is concerned.

Workspace SmartParts have to be Controls (i.e. inherit from System.Windows.Forms.Control at some level). WorkItem SmartParts can be any object.

My personal opinion is that it would have been less confusing if the CAB developers had given the SmartParts collection on the Workspace a different name: ‘ViewsShown’ or something similar might have been better.

Conclusion

This article gave a brief introduction to SmartParts and showed why they can be confusing. It also gave an even briefer introduction to Workspaces. Part 16 of this series of articles will examine Workspaces in more detail, and expand on how we can use SmartParts in our code. It will also look at the SmartPartInfo class.

Advertisements

13 Comments »

  1. Well written article in great depth. Your clear analysis clears my doubts about my past experiences. For example, sometimes I added some views (SmartPart views) through Items, while other times views are added through SmartParts. No matter how they were added, they all can be displayed by a workspace’s Show. [SmartPart] is also a very interesting one. It is optional as far as workspace’ Show() concern.

    Some comments on adding views to SmartParts. AddNew() has an overloaded method which you can specify a key. It is a good practice I think to use a key to add a view and check its existence before adding it. In this way, your call to workspace’s Show will bring a view already displayed to front. Otherwise, you will add a new view and display it on top of existing ones.

    Another comment is that AddNew, I think, will require the view with default constructor. If you have only customized constructors, or you want it being created by specified constructor, you have to create it first and then use Add to add it to SmartParts. Correct me if it is wrong.

    Comment by chudq — October 28, 2007 @ 5:13 am

  2. Hi,
    I am developing a SCSF/CAB project. I know you will be covering scsf later in this series but I need some immediate help. I hope you can help me. When my application loads, first view it shows is a login view. Once the user logs in, he/she interacts with the ui and in the process more views get shown, certain registered ui extension sites get extended, many collections of workitem gets modified and so on.
    Now I want to employ a logout functionality which should be able to bring the application in its ‘initial state'(i.e all the modifications should be cleared off) with login view showing as the first view for the user.
    Can you please suggest me some way.
    Thanks.

    Comment by Amit — October 28, 2007 @ 4:37 pm

  3. Very nice series of articles! I’ve been searching for info on the recommended use of SmartPartPlaceholders. I’m currently experiementing with using a View containing 2 or 3 SmartPartPlaceholders and showing multiple copies of the View in a TabWorkspace. I’ve found I can do this provided that I add separate copies of the sub views with unique IDs to the WorkItem.Workspaces collection using the Add method suggested in comment 1 prior to showing the main View on the TabWorkspace. I have to admit though I’m not certain if I should just be using UserControls on the main View instead of SCSF views hosted inside the SmartPartPlaceholders. My idea was that it would be easier to replace any of the sub views UI if I could leave the sub views presenters alone. I’ve found plenty of information on how to use the SmartPartName property as a means to control which view is shown in a SmartPartPlaceholder but not much about when to use one. Thanks!

    Comment by Chris Smith — October 30, 2007 @ 9:21 pm

  4. Thanks for this fantastic collection but I’m not able to find any index for these tutorials or Links to all these 15-16 blogs. Can you or anybody please send me the link to this page. thanks

    Comment by Vivek — November 2, 2007 @ 12:30 am

  5. Vivek,

    Here is a link to a TOC page for these articles:

    https://richnewman.wordpress.com/intro-to-cab-toc/

    Rich–thank you for your clear and concise review of the CAB/SCSF framework. I am interested in your subsequent articles on this topic…

    Comment by Tom — November 2, 2007 @ 8:00 pm

  6. In an example above you have added deckWorkspace through Toolbox, but dont see those components on my toolbox… how can I make it appear in toolbox and use it in UI designer ?

    Comment by Prashant J — February 9, 2008 @ 9:38 am

  7. This is great! Well thought of and easier to understand. It really clears up a lot of misunderstandings I’ve been having. There is a part that I don’t understand how to use is the SmartPartPlaceholder in any of these Workspaces in a dynamic format. Meaning, I would like to know how to use different smartparts in the same smartpartplaceholder. Is there an example of code available to work from? I would really appreciate it very much.

    Comment by Ronald G Anderson — February 17, 2008 @ 7:37 pm

  8. Can u make a list of all parts?

    Comment by rfcdejong — March 3, 2008 @ 9:34 am

  9. rfcdejong

    There already is a list available – you can access it from the sidebar.

    https://richnewman.wordpress.com/intro-to-cab-toc/

    Rich

    Comment by richnewman — March 3, 2008 @ 2:22 pm

  10. I asked too fast, found it by myself later on..

    But thanks 😉

    Until so far your introduction made alot of things more clear for me.
    The only thing i have problems with is the difference between WorkItemController from the SCSF and a WorkItem itself, but i saw that is in a later part (part 20 till part 25)

    The BankTeller Quickstart is using WorkItems and not the SCSF classes, missing the Model-View-Presenter pattern.

    PS: I couldn’t find a big picture of the complete SCSF and CAB all together.. with the total hierarchie visible and classes combined in each block.

    Comment by rfcdejong — March 7, 2008 @ 10:36 am

  11. Hi m developing SCSF/CAB Project, in this m using leftWorkSpace and rightWorkSpace and i can load 2 views on rightworkspace by clicking on a button placed on a view which is loaded on leftworkspace.My problem is when i click the button twice, thrice or any number of times that many times view on right appears, how to control, so that if button is clicked more than once everytime same view should be highlighted

    Comment by Shobha — May 13, 2008 @ 7:51 am

  12. To get the workspaces and other CAB controls to show up in the ToolBox, you have to create a new “tab” in the toolbox; say “CAB”.

    Then expand “CAB” and right-click and select Choose Items.
    Browse to the Microsoft.Practices.CompositeUI.WinForms.dll file and select it.
    The workspaces ans SmartPartInfo objects will be displayed for drag&drop operations on a winForm.

    Comment by Sheir — May 16, 2008 @ 4:44 pm

  13. Dear Rich

    Pls help me, its urgent and I am newbie to this SCSF.

    I am trying to reuse an existing view and add it to a workspace. Data is coming from server. I am using a deckworkspace and setting data in the view and activate the view. But to wonder, no exceptions, but data is not being displayed 😦

    I am sick, please help me. I know its very silly, but I am newbie 😦

    Appreciate your help.

    Comment by Anonymous — August 19, 2011 @ 1:32 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: