Saturday, December 31, 2011

Modifying Leading and Trailing HTML in SPLongOperation

Long running operations show those nice screens in SharePoint telling the user that something that takes a lot of time is being executed and they have to be patient. These screens can be customized with such a title and a subtitle (LeadingHTML and TrailingHTML) which can be set only once for each operation. But what if I have several steps inside the same long operation, and I want to modify these texts and show the user each of the steps?

I took the idea of my solution from James Boman’s blog, and with a very few modifications I got it working for SP2010. The goal is to create a new custom SPLongOperationEx class that will modify directly the HTML code of the long running operation screen via Javascript. This will override the important methods described in the MSDN page, so we can keep the functionality in our new class.

In the constructor, we add the following javascript code to modifiy in the client-side the text.

<script language='javascript'> 
    var spnTarget = document.getElementById('[Leading or TrailingHTML]')
    spnTarget.innerHTML = \"[Text to change]"\
</script>

Probably you will have now the idea of what you have to do, right? The new class will look then as follows:


public class SPLongOperationEx : IDisposable
{
#region Storage

private SPLongOperation mobjSPLongOperation;
private System.Web.UI.Page mobjPage;
private bool mblnBegun = false;
private string mstrChangeScript = "";
private bool disposedValue = false;
#endregion

#region Properties

public string LeadingHTML
{
get
{
return mobjSPLongOperation.LeadingHTML;
}
set
{
mobjSPLongOperation.LeadingHTML = "" + value + "";
if (mblnBegun)
{
mobjPage.Response.Write(string.Format(mstrChangeScript, "spnLeading", value));
mobjPage.Response.Flush();
}
}
}

public string TrailingHTML
{
get
{
return mobjSPLongOperation.TrailingHTML;
}
set
{
mobjSPLongOperation.TrailingHTML = "" + value + "";
if (mblnBegun)
{
mobjPage.Response.Write(String.Format(mstrChangeScript, "spnTrailing", value));
mobjPage.Response.Flush();
}
}
}

#endregion

#region Constructor

public SPLongOperationEx(System.Web.UI.Page page)
{
mobjPage = page;
mobjSPLongOperation = new SPLongOperation(page);
}

#endregion

#region Public Members

public void Begin()
{
mblnBegun = true;
mobjSPLongOperation.Begin();
}

public void End(string vstrRedirectPage)
{
mobjSPLongOperation.End(vstrRedirectPage);
}

public void End(string vstrProposedRedirect, Microsoft.SharePoint.Utilities.SPRedirectFlags rgfRedirect, System.Web.HttpContext context, string queryString)
{
mobjSPLongOperation.End(vstrProposedRedirect, rgfRedirect, context, queryString);
}

#endregion

#region IDisposable

protected void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
this.mobjSPLongOperation.Dispose();
}
}
this.disposedValue = true;
}

void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

#endregion
}

NOTE: Although it works, I am still having the issue that it is required to duplicate the calling to the setter, because if the new text is set only once, it does not work properly. I guess it is something related to the life-cycle between server and client side… tough stuff anyway.