Infosys Microsoft Alliance and Solutions blog

« Visual Studio 2008 RTM | Main | Visual Studio 2008 Available »

Removing SmartParts from a WorkItem

A common and perhaps a little painful problem while working in CUIB (using SCSF) is disposing the smart parts from a WorkItem. If smart parts are not disposed properly one may get an exception like "Cannot access the object in its current state" and in cases where we are using TabWorkspace it is often difficult to predict the behavior.

The problem here I see is the way WorkItem is understood. A WorkItem is primarily seen as a container to manage objects. The normal tendency is to have all the smart parts added to the WorkItem and later retrieve the SmartPart from the WorkItem to accomplish certain use cases. This is not a good practice to follow; a WorkItem should be seen as something that is used to accomplish a use case e.g. In a TabWorkspace we can have creating a tab as a use case. Each use case needs to be encapsulated in a WorkItemController class. Adding and also disposing of smart parts need to be done in this class.

Let me try to elucidate how this needs to be done-Let us consider a simple use case where on click of a button a tab needs to be created in the TabWorkspace. The tab needs to display a SmartPart. Straight forward of doing this would be something like this-In the Command Handler of the button

//Add the SmartPart to the WorkItem

this.WorkItem.SmartParts.AddNew<View>("MyView1");

//Add more SmartParts as required

//Display SmartPart in TabWorkSpace

this.WorkItem.Workspaces[WorkspaceNames.LayoutWorkspace].Show(this.WorkItem.SmartParts.Get<View1>("MyView1"),new SmartPartInfo("My View","This is my view"));

The problem with this approach will be disposing of the SmartPart from the WorkItem when the use case ends - in our case closing of the tab. Of course we can remove SmartPart from the WorkItem by calling the Remove method on the WorkItem. This is not the right way to as SmartPart would not be completely disposed from the WorkItem and we will run into problems when we try add to the SmartPart again to the WorkItem and in some cases we may experience weird behavior.

The right way to tackle this problem is to encapsulate all the logic pertaining to a use case in a WorkItemController class.

Creating WorkItemController Class to define a Use Case

Create a class say MyController which inherits from WorkItemController class. WorkItemController comes with SCSF and can be found in Infrastructure.Interface Project. All the logic including adding\removing SmartParts pertaining to the use case ought to be there in this class.

class MyController : WorkItemController

{

        public MyController()

        { }

        public void LoadSmartParts()

        {

//Add the SmartPart to the WorkItem

this.WorkItem.SmartParts.AddNew<View1>("MyView1");

//Display SmartPart in TabWorkSpace

this.WorkItem.Workspaces[WorkspaceNames.LayoutWorkspace].Show(this.WorkItem.SmartParts.Get<View1>("MyView1"),new SmartPartInfo("My View","This is my view"));

        }

}

Now in the Command Handler of the button create an instance ControlledWorkItem – this can be found in Infrastructure.Interface and add this instance to the WorkItem

ControlledWorkItem<MyController> myController = this.WorkItem.WorkItems.AddNew<ControlledWorkItem<MyController>>("MyController");

myController.Controller.LoadSmartParts(); 

Disposing SmartParts

Subscribe to the tab close event and in the event subscription code dipose the SmartPart and finally the WorkItem itself.

//Dispose method in the Controller class

//Disposes all the SmartParts from the workItem

class MyController : WorkItemController

{

        public void DisposeSmartParts()

        {

IEnumerator<KeyValuePair<string, object>> smartPartCollection = this.WorkItem.SmartParts.GetEnumerator();

            List<object> spCollection = new List<object>();

            while (smartPartCollection.MoveNext)

            {

KeyValuePair<string, object> namedValue = smartPartCollection.Current;

                  spCollection.Add(namedValue.Value);

            }

            for (Int16 i = 0; i <= spCollection.Count - 1; i++)

            {

                  this.WorkItem.SmartParts.Remove(spCollection(i));

            if (spCollection(i) is IDisposable)

                  {

                        ((IDisposable)spCollection(i)).Dispose();

                  }

            }

        }

}

Call this method in the tab close event subscription code

ControlledWorkItem<MyController> myController = this.WorkItem.WorkItems.Get<ControlledWorkItem<MyController>>("MyController");

//Dispose the SmartParts from the WorkItem

myController.Controller.DisposeSmartParts();

//Dispose the WorkItem

myController.Dispose();

This ensures all the SmartParts and subsequently the WorkItem is disposed correctly.

 

 

 

Comments

Nice post. I knew that those guys(smartparts) were hanging somewhere around but did not know how to dispose them. We are developing stuff using CAB/SCSF and just a few comments how we are doing this. First we are loading smartparts in the Run() method of the WorkItem Controller (What is your opinion about that?)

e.g.
private IWorkspace _workspace;
private IView1 _view1;
public void Run(IWorkspace workspace)
{
//Add the SmartPart to the WorkItem

_view1 = this.WorkItem.SmartParts.AddNew("MyView1");

_workspace.SmartPartClosing += new EventHandler(_workspace_SmartPartClosing);

// We should bring our view to the front when we're activated
WorkItem.Activated += new EventHandler(WorkItem_Activated);
}

void _workspace_SmartPartClosing(object sender, WorkspaceCancelEventArgs e)
{
if (e.SmartPart == _view1)
{
_workspace.SmartPartClosing -= new EventHandler(_workspace_SmartPartClosing);
// Makes it seem faster...
_workspace.Hide(_view1);
WorkItem.SmartParts.Remove(_view1);
// If we should kill the view and the WorkItem:(I knew that the smartpart is not disposed and hanging around!!!)
WorkItem.Terminate();
// BUG: The _view1 is never disposed
}
}
void WorkItem_Activated(object sender, EventArgs e)
{
if (_workspace.ActiveSmartPart != _view1)
{
_workspace.Show(_view1, smartpartinfo);
}
}


and we are calling workitem controller (mostly from ModuleAction because of the security implementation) for example like this:

string workItemId = null;
workItemId = "MyView1";
ControlledWorkItem alWorkItem = null;
if (_workItem.WorkItems.Get(workItemId) != null)
{
alWorkItem = _workItem.WorkItems.Get>(workItemId);
}
else
{
alWorkItem = _workItem.WorkItems.AddNew>(workItemId);
alWorkItem.Controller.Run(_workItem.Workspaces[WorkspaceNames.MainWorkspace]);
}
alWorkItem.Activate();

I'll be very greatfull if you can put comment on the code above. Thanks & regards

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter