Introduction
This article is all about providing Context Sensitive Help in a webpage, asynchronously or AJAX style, using Script Callbacks in .NET 2.0.
Scenario in brief
When you are filling an online form in an internet website, or while trying to enter the search\filtering criteria for various fields, one time or other, every one of us might have definitely expected for a brief description or help on what we are expected to do. Providing such information on demand would definitely make the user very comfortable.
This article on Context Sensitive Help deals on how it can be accomplished.
The following sequence of steps describe the scenario in brief:
- You are trying to fill a form in a website.
- You see a screen element\form field to enter the value for.
- You are perplexed or confused.
- You move the mouse over the element name.
- The cursor symbol reveals your expression. A question mark is shown next to the cursor.
- Click on it.
- Dynamically, Help appears, that is help\description related to the screen element clicked.
Problem
How to fetch the Help text dynamically?
Solutions I have seen
Solution 1
I have seen some websites refreshing or posting back the page when the user clicks on an element, to provide the description or help text in a predefined separate region. This is definitely a heavy approach. Consider a help text that contains just 10 words. We are posting some kilo bytes of data to just fetch a few bytes of data.
Solution 2
Another solution I have seen is load the help text of all the screen elements while loading the page and store at client side in a JavaScript array. On the click event of each screen element, a JavaScript call to fetch the help text from the array is made, passing the index of the screen element in hand as the parameter. This seems to be OK for a page where there are only a few screen elements, but the downside comes for web pages where the number of screen elements runs to hundreds. I have seen an application with a page that has got 104 fields and the help text for each field is not less than 4 lines of 15 words each. So just imagine the size of the help text to be loaded along with the page. Definitely, a very heavy task! Googling for Context Sensitive Help provides you with many other solutions which more or less do the same.
Solution provided in this article
When the user clicks on a screen element for help, an asynchronous call is made to the server using the Client Call back features of .NET, the required help text is retrieved from an XML file located on the server, and returned to the client. There is a round trip to the server, but the entire page is not posted and there is no flickering as well.
Behind the scenes
When we type a URL in the address bar and press the Enter button in the browser, the browser runs an HTTP command, opens a socket with the specified IP address, sends the packets, and waits for the response. The response is displayed by the browser. The same process is repeated when the page is posted back. In this case, the page output is clearly re-displayed. Now we do not want to post the whole page to the server and refresh the entire page. We just want to dynamically display\change\update a part of it, getting the data from the server with out causing any flickering. So, we have two challenges here.
- Establish an HTTP connection with the required IP address to get the data.
- Refresh the page dynamically.
The first task is handled by .NET 2.0 script callbacks, and the second task is handled by DHTML.
So, all that we have to do is:
- The
ICallbackEventHandler
should be inherited by the Webpage.
- Get the Callback Event Reference and register the Client Script Block.
- Implement the
RaiseCallbackEvent
method of the ICallbackEventHandler
.
To use ASP.NET 2.0 script callbacks, you need to implement the ICallbackEventHandler
Interface. The reason for this is discussed in the later part of the article. The trigger element of a page, that is the element of a page which fires some event and for which we need to fetch data from the server, needs to be bound to some JavaScript code which will retrieve the input data from the current page and provide a call to the system provided script function. This function opens an HTTP connection to the corresponding remote ASP.NET page. The ASP.NET runtime detects a callback invocation, and checks to see if the page implements the ICallbackEventHandler
. If implemented, the runtime executes the RaiseCallbackEvent
method on the page. The return values of the server side method is passed back to the client as a response to the previous request. On the client, the response gets passed to a user-defined JavaScript callback function that can then update the user interface via DHTML. As it can be observed, the roundtrip occurs but the whole page is not posted back. The good thing here is that the user can continue working with the controls in the page while the request is being processed in parallel.
Every thing in one paragraph is like swallowing a pizza piece at once. Let us bite the pizza in small pieces and enjoy the taste.
Code walkthrough
The process described above is explained with pieces of code as below:
Inheriting the ICallbackEventHandler
interface by the page:
public partial class _Default : PageManager, ICallbackEventHandler
{
………………………
…………………..
…………………..
}
Getting the reference to the system provided script:
strHelpReference = Page.ClientScript.GetCallbackEventReference(this,
"arg",
"JavaScriptCallBackHelp",
"context"
);
Registering this client script block:
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"CallBackHelp", strHelpScript, true);
This JavaScript method is bound to some event in the page. For example, the click event of the label\text “title:” on the page is associated to the GetContextSensitiveHelp
JavaScript method which triggers the CallBackHelp
JavaScript method. As given above, this holds the reference to the system generated script method that serves the callback. Once the processing is done at the server side, the JavaScript method JavaScriptCallBackHelp
is triggered, passing the result returned from the server-side method as the parameter.
The code for retrieving the help text associated with the element clicked is written in the RaiseCallbackEvent
.
public void RaiseCallbackEvent(string strEventArgument)
{
try
{
char[] charSeperator = { '#' };
if (strEventArgument.StartsWith("Help"))
{
strCallBackResult = LoadContextHelp(strEventArgument.Remove(0, 4));
}
}
catch (Exception ex)
{
Response.Write(ex.ToString());
}
}
The LoadContextHelp
method is the one that actually fetches the help text associated with the screen element. In the attached code, I am fetching the help text from an XML file. This method can be implemented in any other way you wish.
The result is assigned to the string variable strCallBackResult
, which is returned by the method GetCallbackResult
. The triggering of the RaiseCallbackEvent
and the GetCallbackResult
methods is done by the runtime.
Overview of other components in the application attached
A user control HelpControl
is created to hold the markup of the Help section.
A separate class XmlMessages
is created. This holds the path to the XML file, and the whole XML context is an XmlDocument
object which is exposed as the property of the XmlMessages
class. Various methods are provided to fetch the required node\nodes from the XML file using Xpath
.
A separate class CacheManager
is created to provide methods to load and refresh the cache data.
A separate PageManager
class is to be inherited by all the web pages in the application. In the Init
method of this class, the CacheManager
methods are triggered to load the data from the Cache or refresh the data in the Cache.
Details on the JavaScript functions used
The title and description of the help text is returned in the same parameter, concatenated by the character “#”. The JavaScript callback method JavaScriptCallBackHelp
splits the returned result in to Title and Description, and triggers the ShowHelp
JavaScript function in the helpfunctions.js JavaScript file.
function JavaScriptCallBackHelp(help, context)
{
var helpArray = new Array();
helpArray = help.split('#');
ShowHelp('objHelpControl',helpArray[0],helpArray[1]);
}
The JavaScript function ShowHelp
sets the div
related to the title and the description, and changes the style of the container div
to block
to display the help.
function ShowHelp( strPrefix, strTitle, strMessage )
{
var objContainer = document.getElementById(strPrefix + '_csh_container');
var objTitle = document.getElementById(strPrefix + '_csh_Title');
var objHelp = document.getElementById(strPrefix + '_csh_Description');
objContainer.style.display = 'block';
objTitle.innerHTML = strTitle;
objHelp.innerHTML = strMessage;
}
When the cross image is clicked, the function SetHelpClosed
is triggered to close the help section. This is done by changing the display style of the container div
to none
.
function SetHelpClosed(objElement) {
objParentObject = objElement.parentElement;
while (objParentObject.id=="" && objParentObject != null)
objParentObject = objParentObject.parentElement;
if (objParentObject == null) return false;
objParentObject.style.display="none";
}
There are a few other JavaScript functions that are used for making the help section draggable. The details of the same are not in the scope of this article.
Points of interest
In the attached code, in addition to Script Callbacks, one can observe the practical implementation of various concepts like:
- Base class implementation
- Caching
- Themes (using skin file for the controls)
- Using the
XmlDocument
object and Xpath
- User Controls
Conclusion
The Context Sensitive Help feature might not be applicable to all web pages. This feature should be provided based on the type of webpage and jargon used in the webpage. For example, in some business specific intranet web sites like Insurance, where there will be hundreds of elements in a single webpage, implementing this feature would be very appropriate. Implementing this feature should not be based on the number of screen fields any way. Even if there are only a few fields, if it is required, yes, definitely provide it.