Divide by zero
 Thursday, June 19, 2008
VSTO solution for context sensitive dynamic help in Word 2007

A project I developed called for context sensitive dynamic help inside of Microsoft Word, based on the current cursor location. The idea was that if the cursor is on an image, then help information would be presented regarding the business rules pertaining to images. I.E. what size can the image be, policies regarding alternative text for web publishing etc. To achieve this there were two components. One was a Quality Assurance component to check for conformance to business rules. The other was the dynamic help system. I present a whittled down version of the help system here. The Quality Assurance component will be discussed in a future entry. There is everything necessary to implement this as a full fledged context sensitive dynamic help system. The help information is presented in a custom task pane, and uses HTML.


Figure 1 -Word 2007 showing the Context sensitive dynamic help system

Architecture

This solution uses three main components:

  • The VSTO addin
  • A MessageBroker which receives events from the VSTO addin
  • The Help UserControl which consumes events from the MesageBroker

This centralised messaging architecture is beneficial when other components are introduced such as the QualityAssurance component referred to earlier. The VSTO Addin wouldn't need to be changed, the MessageBroker, the QualityAssurance component would subscribe as listeners for the events raised by the MessageBroker. Another advantage of this approach is that security and logging would be easily implemented in the one location.

So, when the Addin loads, the WindowSelectionChanged event is subscribed to by the addin.

this.Application.WindowSelectionChange+= new Microsoft.Office.Interop.Word.ApplicationEvents4_WindowSelectionChangeEventHandler(
   Application_WindowSelectionChange);

It would be possible for the Help UserControl to subscribe directly, but by doing it this way we can define a few rules around what events get triggered, and also cache the current style so that the event is not consumed by every registered listener every time the cursor moves even though the style hasn't changed.


void Application_WindowSelectionChange(Microsoft.Office.Interop.Word.Selection Sel) {
     Word.Style style = (Word.Style)Sel.get_Style();
     if (! style.NameLocal.Equals(_currentStyle)) {
        _currentStyle = style.NameLocal;
        _messageBroker.Publish(typeof(MessageEventArgs), Sel.Paragraphs[1], style);
     }
}

Note in this code how it is important to retrieve an instance of the style object and cast it to type Word.Style. This is COM Interop in Word. It would be possible to write an extension method to get around this, but I don't think its necessary for this example.

The constructor for the Help UserControl accepts an instance of the MessageBroker. Note: I should probably make this class a Singleton.

The Help UserControl receives event notifications when the current style has changed. It loads a web browser control and loads an HTML page named after the style. if no page exists for that style, it will display a default help page. There is a tabbed interface, with an Infoand aHelp tab. The Help tab has not been implemented, the idea was to provide links to examples.

I have only included stripped down versions of HTML pages for three styles:

  • Heading 1
  • Heading 2
  • Heading 3

It is simply a matter of creating a page witht the style name, and an extension of .html and dropping it in the help folder. The code that loads the page looks like this:

private void LoadHelpIntoBrowser(string currentStyle) {
    String helpFilePath
=
CreateHelpFilePath(currentStyle);
    
if
(File.Exists(helpFilePath)) {
       webInfo.Navigate(helpFilePath);
    }
else
{
       LoadHelpHomePage();
    }
}

private void
LoadHelpHomePage() {
   string p =
Path.Combine(Path.GetDirectoryName(
   System.Reflection.Assembly.GetExecutingAssembly().CodeBase),
"..\\..\\help"
);
   String helpFilePath
= CreateHelpFilePath("WordJester"
);
   webInfo.Navigate(helpFilePath);
}

private
String CreateHelpFilePath(String style) {
   Uri baseUri
=new Uri(
      Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase));
   
return String.Format("{0}{1}..{1}..{1}help{1}{2}.html"
, baseUri.LocalPath,
         Path.DirectorySeparatorChar, style);
}

The beauty of this approach is that you can add in as many styles as required, even non standard styles, and as long as a help file exists, it will be displayed. The file names will look like this:


Figure 2 - file names for the HYML pages based upon the word style name

Run the solution, and try creating some heading levels 1, 2 and 3, and move the cursor between them and check out the help in the custom task pane change. Let me know what you think of this application.

WordJester.zip (224.21 KB)


Thursday, June 19, 2008 10:37:37 PM (AUS Eastern Standard Time, UTC+10:00)   #    Comments [0]  Downloads | VSTO | Word 2007
link to del.icio.us link to reddit link to StumbleUpon link to Facebook Bookmark to Google