Currently there’s no specific standard for presenting help in the Web environment. In this entry I’ll describe the approach I use for displaying Help in my Web applications which is based mostly on a page based approach to help topic display.
I build my help content with our own tool Help Builder, so my Help Content for each application is always available online. From within Help Builder I just build my help content and use the upload tool to get it to an FTP directory on the Web Server.
So, how do I hook this up to my Web applications? Well, the content is just plain HTML so it’s easy to link to pages directly of course. Since the Help Content is mere HTML it’s just a matter of adding a hyperlink to the proper help topic.
For example, if I want to link to my West Wind Web Store’s Help and display a specific topic like the new subitem support it might look like in the figure below. Note that there are two separate links on the page, one in the header which is page specific help and one in the content which is specific.
The link itself links to a help topic like this:
http://www.west-wind.com/westwindwebstore/docs/index.htm?page=_14x0vbf7w.htm
or more simply:
http://www.west-wind.com/westwindwebstore/docs/_14x0vbf7w.htm
The latter just displays the topic while the former displays the topic and the content tree generated by Help Builder as well as expanding the tree for the currently selected topic.
It’s easy enough to put this whole nasty URL into your application either as a full or relative Url, but that’s probably not what you want to do. Instead it’s much cleaner to abstract the help url and use a function or class method to provide the Url formatting and merely use a page name to manage the link display.
One really simple way to do this is to add a method to a utility class that handles the formatting for you including placement of the image and text label that make up the Help object. Here’s a static C# method I usually add to my projects that handle HTML display in ASP.NET pages:
/// <summary>
/// Returns a fully qualified help url from a topic link
/// </summary>
/// <param name="HelpTopic"></param>
/// <returns></returns>
public static string HelpTopic(string HelpTopic,bool NoHelpText,string AltText)
{
string HelpUrl = null;
if (HelpTopic == "")
HelpUrl ="http://www.west-wind.com/westwindwebstore/docs/";
else
HelpUrl = "http://www.west-wind.com/westwindwebstore/docs/index.htm?page=" +
HelpTopic;
if (AltText != null && AltText != "")
AltText = " alt=\"" +AltText + "\" ";
else
AltText = "";
return "<a href='" + HelpUrl +
"' style='text-decoration:none' target='WebStoreHelp'><img src='" +
wwWebDataHelper.FixupUrl("~/images/help.gif") + "' border='0'" + AltText+ ">" +
(NoHelpText ? "" : " Help") + "</a>";
}
public static string HelpTopic(string HelpTopic)
{
return WebStoreUtils.HelpTopic(HelpTopic,false,null);
}
Because the method is static you can reference this method easily from within an ASPX page using stock ASP style syntax:
<%= WebStoreUtils.HelpTopic("_14x0vbf7w.htm") %>
This displays the Help icon and text at the top of the form in the figure above. Alternately here’s the more complete form that hides the label and adds an Alt tag is shown in the selected item on the same figure.
<%= HelpTopic("_1ic13yjx5.htm",true,"Subitem Help") %>
The advantage of this approach over specifying a full Help Url should be clear: You get a consistent way that your Help topics are referenced and display and most importly you can change the way the help links are displayed without having to fix all your pages. If your help content moves all you do is change this one function. You can further abstract that part by storing the HelpBaseUrl in a configuration file to totally externalize the Help Url.
Why an ASP style tag, rather than a Web or User Control? I thought about that but it turns out getting a control onto a form is actually more work than adding this simple tag. With a control you need to make sure the control is on the Toolbox or you need to type the control syntax which in either case is more involved. And after all <%= %> tags are still a valid tool and much more efficient than forcing control logic for something as simple as this. Keep it simple.
Integration into a Page framework
If you do want more abstraction you can run this code into your custom Page framework. I have this logic tied into my custom base Page subclass which allows a few more options that you can’t do with just a static function.
One thing that is kinda cool is the fact that IE at least supports the onhelp event on the document, so you can pop up your help topic when the user presses F1. It also works with Mozilla with some quirks. More on that in a minute.
Ok, so the idea is this:
- Add a HelpTopic property to my custom base Page class
- Add support for F1 in IE (and to some degree in Mozilla)
- Provide URL and HyperLink formatting for the Help Url as shown above
The property is self explanatory what’s nice is that this property is now visible in the designer and can be set from there (or the Page_Load()) . If set that’s the trigger for the code to generate the script code to hook up the client side onhelp or onkeydown event handlers, which can be done using RegisterClientScript(). The script code I use is shown in the code snippet below.
public class wwWebForm : Page
{
/// <summary>
/// Page level Help Topic - enables
/// </summary>
public string HelpTopic
{
get
{
return _HelpTopic;
}
set
{
_HelpTopic = value;
}
}
protected string _HelpTopic = "";
/// <summary>
/// Override to attach the HelpTopic script code
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if ( this.HelpTopic != null && this.HelpTopic != "")
this.RegisterStartupScript("HelpHandler",
@"<script>
function OpenHelp(e)
{
window.open('" + this.FormatHelpUrl() + @"','WebStoreHelp','toolbar=no,top=10,left=10,width=600,height=600,resizable=yes,status=yes,scrollbars=yes');
return false;
}
function KeyDownHandler(e)
{
if (! e ) // IE uses event object
e = window.event;
if (e.keyCode == 112) // F1
{
OpenHelp();
e.cancelBubble = true;
}
}
if ( window.navigator.userAgent.indexOf('MSIE') == -1 )
document.onkeydown = KeyDownHandler;
else
document.onhelp = OpenHelp;
</script>
");
}
/// <summary>
/// Method used to format a Help Url into a fully qualified help Url
/// </summary>
protected virtual string FormatHelpUrl()
{
// *** return "/help/" + this.HelpTopic;
if (this._HelpTopic == "")
return App.Configuration.HelpBaseUrl;
return App.Configuration.HelpBaseUrl + "index.htm?page=" + this._HelpTopic;
}
/// <summary>
/// Returns a fully HREF string that can be embedded into a page
/// with <%= this.ShowHelpLink() %>.
///
/// Assumption: ~/images/help.gif exists for a help icon
/// </summary>
public virtual string GetHelpHyperLink(string HelpTopic,bool NoHelpText,
string AltText)
{
if (HelpTopic == null)
HelpTopic = this._HelpTopic;
if (HelpTopic == "")
return "";
// *** Retrieve formatted Url
string HelpUrl = this.FormatHelpUrl();
// *** Add Alternate text if provided
if (AltText != null && AltText != "")
AltText = " alt=\"" +AltText + "\" ";
else
AltText = "";
return "<a href='" + HelpUrl + "' style='text-decoration:none' target='WebStoreHelp'><img src='" +
wwWebDataHelper.FixupUrl("~/images/help.gif") + "' border='0'" + AltText+ ">" +
(NoHelpText ? "" : " Help") + "</a>";
}
public string GetHelpHyperLink(string HelpTopic)
{
return this.GetHelpHyperLink(HelpTopic,false,null);
}
public string GetHelpHyperLink()
{
return this.GetHelpHyperLink((null,false,null);
}
… other Page base subclass methods
}
FormatHelpUrl()’s job is to format the URL properly for our help file essentially appending the HelpTopic specified to the end of the ‘base’ url. The logic here is a bit specific to my own Web framework which always uses an App.Configuration object which contains a HelpBaseUrl property. This property contains the base help path in this case:
http://www.west-wind.com/westwindwebstore/docs
FormatHelpUrl() can be used directly by your application if you want full control and display a help Url directly. It also gets used by GetHelpHyperLink() which takes the URL generated by FormatHelpUrl() and creates a fully qualified Hyperlink HTML string that can be directly embedded into a page. There are few options on the worker function such as whether the text is displayed or not and whether there’s an Alt tag added with text for the image. You can see the result of the Alt text in the figure above.
These two functions basically provide the same functionality as the static function I showed earlier and its use is very similar:
<%= this.GetHelpHyperLink("_14x0vbf7w.htm ") %>
or – if the HelpTopic property is set – you don’t need to specify the page name at all.
Note that there’s a dependency here on ~/Images/help.gif which is the image for the help icon. Again, this is something I do in all of my apps by default – there’s a set of about 15 or so icons that always end up in the images directory.
Both methods are marked virtual so they are meant to be overridden in your application specific Page classes. My hierarchy for page class inheritance usually has an override of the Page class that’s general and used in all apps, and an application specific version. The application specific version would normally override this functionality if needed.
Handling F1
As I mentioned the code above handles bringing up page level help by pressing the F1 key. The code does this by using the RegisterClientScript() to add block of script code that hooks the onhelp event from Internet Explorer and the onkeydown for other browsers. In IE this logic works great and pops up a new window in response to the a help request. In other browsers a window will also pop up (assuming popup blocking does not get in the way!) but the standard help content window will also pop up.
I’ve not been able to find a way to have onkeydown stop processing the key after it’s been handled. F1 is a special key and appears to override all attempts to cancel the operation either in IE or Mozilla. In IE the only way to override is by using onhelp which is designed for this behavior. Another annoying Mozilla shortcoming.
It would also be cool if there was some way to attach help topic information to particular controls on a form. Unfortunately there’s no way that I can see to extend exisiting HTML controls to have extended fields and there aren’t really any generic fields that could be drafted for this purpose.
Maybe in the future when browsers get a decently extensible object model we can hope to get more control the UI to perform tasks like this.