Archive for August, 2008

CAB & SCSF: Maintaining Active View across Multiple workspaces

Recently I ran into a problem where I have multiple windows open in my application at once. This is great for presenting multiple views to the uesr at one time, but it is not so good for a developer trying to figure out what to do when a common toolbar command is clicked. I found some help on the internet by Chris Holmes who had come across a similar problem and thus created a solution. See: Chris Holmes - CAB: Solving the Active View Problem Chris’ solution however was only good for watching one workspace at a time. And my problem was that if I switched between MDI tabs in the MDI workspace everything was fine. But if I switched from a tab in the UltraMDITabWorkspace to a pane in the UltraDockWorkspace. The smartpart activated command never fired.

So I posted my question over in the SCSF forum and Mariano Converti replied back with his solution. Now this was great this is exaclty what I was looking for. With a little tweaking I could make this work. One problem though: Marianos solution relies on getting the workspace as a Control. Now this is great for all of the out of the box CAB Workspaces. I however am using Infragistics Workspaces, which do not derive from System.Control. So what I have here is the first of a series of posts on this subject. The code below is a service that I created in Infrastructure.Library. I will also post the interface for the service and a sample implementation. I am also going to be posting a modified version of Chris Holmes UIService that manages toolbars and menu items for an infragistics ribbon.

ActiveWorkspaceViewService.cs


[Service]
public class ActiveWorkspaceViewService : IActiveWorkspaceViewService
{
[EventPublication(EventTopicNames.ActiveViewChanged, PublicationScope.Global)]
public event EventHandler<ActiveViewChangedEventArgs> ActiveViewChanged;

IUIService _uiService;

[InjectionConstructor]
public ActiveWorkspaceViewService([ServiceDependency] IUIService uiService)
{
_uiService = uiService;
}

#region IActiveWorkspaceView Members

/// <summary>
/// Registers a Workspace to be monitored for changes such as SmartPartActivated and
/// then watches the controls for the OnEnter event
/// </summary>
/// <param name="workspace"></param>
public void AddWorkspaceToMonitor(IWorkspace workspace)
{
workspace.SmartPartActivated += new EventHandler<WorkspaceEventArgs>(OnSmartPartActivated);

if(workspace.ActiveSmartPart != null)
ActiveView = workspace.ActiveSmartPart;

}

private object _activeView;

/// <summary>
/// Returns the active SmartPart as an object
/// </summary>
public object ActiveView
{
get { return _activeView; }
private set
{
_activeView = value;
_uiService.SetActiveView((IView)value);
OnActiveViewChanged(new ActiveViewChangedEventArgs(((IView)value).Guid));
}

}

#endregion

/// <summary>
/// Handles the SmartPartActivated event for the Workspace
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnSmartPartActivated(object sender, WorkspaceEventArgs e)
{
if (e.SmartPart != null)
{
ActiveView = e.SmartPart;
Control c = e.SmartPart as Control;

c.Enter -= new EventHandler(OnEnter);
c.Enter += new EventHandler(OnEnter);

#if (DEBUG)
Console.WriteLine("SmartPart Activated: " + c.ToString());
#endif
}
}

/// <summary>
/// Provides a method of setting the smart part to the "Active"
/// SmartPart in the UIService by handling the OnEnter event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnEnter(object sender, EventArgs e)
{
Control cntrl = sender as Control;

if (cntrl != null)
{
ActiveView = cntrl;
#if (DEBUG)
Console.WriteLine("SmartPart Entered: " + cntrl.ToString());
#endif
}

}

protected virtual void OnActiveViewChanged(ActiveViewChangedEventArgs eventArgs)
{
if (ActiveViewChanged != null)
{
ActiveViewChanged(this, eventArgs);
}
}

}

IActiveWorkspaceViewService 

This is the interface for the class above.

[c#]
public interface IActiveWorkspaceViewService
{

void AddWorkspaceToMonitor(IWorkspace workspace);

object ActiveView { get; }

}
[/c#]

Now to use the service you need to register your workspaces with the service when the application starts. Make the following changes in your ShellApplication.cs file:

Put the following services in the AddServicesMethods()
[c#]RootWorkItem.Services.AddNew&amp;amp;amp;amp;amp;lt;ActiveWorkspaceViewService, IActiveWorkspaceViewService&amp;amp;amp;amp;amp;gt;(); [/c#]

The next bit of code needs to be placed in the AfterShellCreated function. This will actually register the workspace for use in the service.

[c#]/// Add the workspaces to the ActiveWorkSpace Monitor
/// This will ensure that we always know the active smartpart even
/// if switching between different workspaces
///
_activeWorkspaceService = RootWorkItem.Services.Get&amp;amp;amp;lt;IActiveWorkspaceViewService&amp;amp;amp;gt;();
_activeWorkspaceService.AddWorkspaceToMonitor(RootWorkItem.Workspaces[WorkspaceNames.MainContent]);
_activeWorkspaceService.AddWorkspaceToMonitor(RootWorkItem.Workspaces[WorkspaceNames.MainDock]); [/c#]

Like I said earlier, this is just one piece of the puzzle to make dynamic toolbars, menus and commands work across your entire application. I will post more details, source code, and a complete sample project soon.