Home >
Writing Contextual Help Menus in Flex
Contexual help windows are a common way of implementing context-sensitive help in enterprise applications. These components that need to be instantiated over the main screen in a variety of places in many enterprise level applications. They're usually instantiated from a simple help button that is placed in a variety of locations around the application using whatever layout scheme makes the most sense for that area. While the contextual help pop-up itself is very similar, the placement of the help button needs to remain very flexible.
The most straight-forward approach to this would be to create some kind of static or global method call that each button could make to instantiate the contextual help menu. Each button would have a short click handler that would be used to bring up and place the window and then load it with text, using the static method to bring the window into existence. This is the approach I've seen used on several projects I've been involved in over the past few years.
This approach is okay, but it leads to a lot of rather rote, tedious and duplicated code to maintain. With a little tweaking, it can be made far more flexible and implemented with far easier.
First, if you spend a little time thinking about the architecture you'll see that the code required to launch the pop-up window is more or less the same everywhere. This can be abstracted quite simply. Rather than using the built-in button component for all instances of our contextual help button, we make our own subclass of button that contains the code required to launch the window.
<?xml version="1.0" encoding="utf-8"?>
<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
/>
Throughout the rest of this post I'm going to show you a few snippets of code that explain the structure of using this approach. This is by no means a complete demo and many of the methods are meant to represent implied structure in ways that should be obvious. Now back to the example.
Our first concern is how this button will cause the help window to be display. This class could just launch the pop-up itself, but that's not the most memory efficient way we can do things - let's re-use the same pop-up over and over through some kind of external class. Our button could call a static method on a helper class like we talked about before, or dispatch an event to pass the data to another component. Using an event fits well into the IOC frameworks I've been using lately so that's the approach I'm going to take here.
There are three more pieces of data we'll need to know before we can launch our help window: the coordinates to place it, the size of the original button, and any text that the window needs to display.
For my example, let's assume you always want to play the contextual help window near the contextual help button that was used to launch it. This makes sense, since "contextual" implies that the help window should be placed near the content that it is providing help for. We can pass the coordinates and size of the original button easily by using the "getBounds" method attached to every object in the Flash framework. We pass it the stage as the parameter, indicating we want the x/y coordinates in stage-space, and it returns to us a rectangle object describing the size and location of the object. The click handler inside of our button class will use this rectangle to do the math needed to display our pop-up on the screen. Two down, one to go.
Strings that populate the actual contextual help should be extracted to a single location and loaded through a resource manager. Different implementations of the contextual help window system may require more or less text, but in general they all seem to contain some text for the title of the window and some text for the body. All we really need is some kind of key to look these objects up by.
To provide this key I'm going to add one parameter to our button and call it "key." You could use this to look up the strings in a Dictionary object, request them from some kind of static helper class method, or just pull them from a resource bundle.
<?xml version="1.0" encoding="utf-8"?>
<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
public var primaryHelpKey:String="";
]]>
</fx:Script>
</s:Button>
In my particular implementation I'm going to assume that most components have only one contextual help button. If this is true they don't really need to provide a key, since we could use the name of the parent document. Look this up using the qualified class name of the object contained in the document property for your button class. The qualified class name contains the entire string - split on the "::" character to get just the class name. Here's the final method:
public function onClick(event : Event):void{
if(key == "")
key = getQualifiedClassName(view.document);
key = key.split('::')[1];
}
var helpEvent:HelpEvent = new HelpEvent();
//pass any strings you need into the event however you choose
helpEvent.helpText = ResourceManager.getHelpText(key);
helpEvent.buttonBounds = getBounds(this.stage);
dispatch(helpEvent);
}
Finally, for completeness, here's the method that handles the helpEvent and displays the contextual help window. This method would go on your static helper class, a mediator on your main object, or something like that. In this method I display the pop-up window and then position it so that it is displayed next to the corresponding contextual help button.
public function openContextHelpWindow(event:HelpEvent):void{
if(!contextHelp){
contextHelp = new ContextHelpWindow();
}
//set the title and body text for the new contextual help window
contextHelp.helpText = event.helpText;
//add the contextual help window to the screen
PopUpManager.addPopUp(contextHelp,
FlexGlobals.topLevelApplication as DisplayObject);
//position the window appropriately so that it remains close
//to the button that spawned it but doesn't go off of the screen
//in any direction.
contextHelp.y = event.buttonBounds.y;
if(contextHelp.y+contextHelp.height >= stage.height){
contextHelp.y -= contextHelp.height - event.buttonBounds.height;
}
contextHelp.x = event.buttonBounds.x + event.buttonBounds.width;
if(contextHelp.x + contextHelp.width >= stage.width){
contextHelp.x -= contextHelp.width + event.buttonBounds.width;
}
}
And we're done! Let's cover what we've done and what this saves us in implementation.
Under the "straight forward" approach, anyone who wants to add a new contextual help window to the system will need to:
1. instantiate a new help button in their code
2. write a click handler for the button
3. find the content needed to populate the contextual help window
4. produce the help-window pop-up
5. populate the help-window
We've create our own button class to handle steps 2, 4 and 5. By enforcing a convention and providing an override key we've taken care of step 3. This means that the new system for adding a new contextual help window is:
1. Write the text required for the contextual help window externally (in a help class or resource bundle, which you should've been doing anyway.)
2. Instantiate a new button
...and that's it. The instantiation looks like this if there's only one in the document:
<ContextualHelpButton/>
If there are more than one, we provide a custom key to look up the text values with:
<ContexutalHelpButton key="keyName"/>
And that's it. This much easier system required very little overhead to write and results in less code that's easier to maintain.
This approach is similar to those used in the framework for things like tool-tips and data-tips and can obviously be abstracted to many other applications.




Facebook Application Development
Comments
Leave a comment