Introduction
I have seen many people crying on forums for functionality for running multiple tasks by a single user while using the User Interface Process Application Block (UIPAB).
Especially if it was a web application (in fact, every question did mention a web application). The official documentation of UIPAB suggests implementing the ITask
interface
and explicitly handling situations where multiple tasks are to be simultaneously run by a single user (no code example has been provided though). This article deals with this
very problem by making some small changes to the code of UIPAB. Note that UIPAB is available for download at Microsoft’s Patterns & Practices website.
The hidden problem
There is one basic problem in achieving a multiple windows (with multiple tasks) scenario with UIPAB.
UIPAB stores the ActiveTaskID
in the session variable which is shared by all browser instances from the same machine. So inherently, UIPAB can have one task active
at a time (it saves the states of all the tasks but keeps only one of them active at a time). The ActiveTaskID
is used in the Load
event handler
of the WebFormView
class in order to retrieve the state of the task.
The solution
With this behavior, UIPAB can have only one task active. What we need is some way to have multiple tasks active at a single instance.
This leads us to an obvious conclusion that we cannot store the ActiveTaskID
in session. We need to store it with the page. I thought of storing it in the
ViewState of the page. But with all the navigations happening through Response.Redirect
, it becomes impossible to store the TaskID
in the viewstate.
Then, I decided to put the taskID in the query string of the Page Request. I had to make two changes in the code provided by Microsoft to make this happen:
- Change in
WebFormView
class: replace the default GetSessionMoniker
method with the following:
private SessionMoniker GetSessionMoniker()
{
SessionMoniker sessionMoniker = null;
try
{
sessionMoniker = SessionMoniker.GetFromSession(new Guid(Request.QueryString["taskId"]));
}
catch(Exception ex)
{
throw new UIPException(Resource.Exceptions.RES_ExceptionTaskNotFound);
}
return sessionMoniker;
}
WebFormViewManager
class: replace the ActivateView
and RedirectToNextView
methods with the following code:
public void ActivateView( string previousView, Guid taskId, string navGraph, string view )
{
SessionMoniker sessionMoniker = new SessionMoniker( navGraph, view, taskId);
sessionMoniker.StoreInSession();
ViewSettings viewSettings = UIPConfiguration.Config.GetViewSettingsFromName( view );
if( viewSettings == null )
throw new UIPException(
Resource.ResourceManager.FormatMessage(
Resource.Exceptions.RES_ExceptionViewConfigNotFound, view ) );
HttpContext.Current.Session[WebFormView.CurrentTaskKey] = taskId.ToString();
RedirectToNextView(previousView, viewSettings, taskId);
}
private void RedirectToNextView(string previousView, ViewSettings viewSettings, Guid taskId)
{
try
{
if (previousView == null)
HttpContext.Current.Response.Redirect(
HttpContext.Current.Request.ApplicationPath +
"/" + viewSettings.Type +
"?taskID=" +
taskId.ToString(),
true);
else
HttpContext.Current.Response.Redirect(
HttpContext.Current.Request.ApplicationPath +
"/" + viewSettings.Type
+ "?taskID=" +
taskId.ToString(),
false);
}
catch (System.Threading.ThreadAbortException) { }
}
Using the code
Use the code as it is. You can directly start using the modified UIP code by downloading it. Or you can make the changes yourself to the UIPAB code downloaded from Microsoft’s website.
Note
The modifications done to UIPAB are solely by me and Microsoft has nothing to do with it. In case of any problems faced due to use of this code (specifically the features that
were modified), Microsoft may not be held responsible for it. Commercial use of the code should be done only after thorough testing. I do not claim any responsibility
for any erroneous behavior arising out of the use of the included code.
This article uses UIPAB 2.0. The same changes can be applied to UIPAB 1.x as well but I have not personally done so.