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.