Creating Smart Interfaces with Smart Tags and VFP 7
As developers, we're used to data sitting in tables and databases.However, this is not how data exists in the real world?it's merely how developers want data to appear. Typically, data resides in documents and forms, emails and spreadsheets. Smart Tags are Microsoft's first serious attempt to utilize that kind of information and make it available to knowledge workers in a sensible fashion.
A full bio can be found on the web at: www.eps-software.com/MarkusEgger
With Office XP, Microsoft introduced a new technology called Smart Tags. No doubt this technology is fascinating, but that also left me wondering what to do with it. I watched Microsoft demo Smart Tags on several occasions; typically, the presenter demonstrated Smart Tags by typing a stock symbol (such as "MSFT") into Microsoft Word. Word then recognized the symbol and underlined the symbol using a red dotted line. When the mouse was moved over the symbol, a little icon appeared and, when clicked, it presented a menu with action items that are related to trading stock (see Figure 1).
Figure 1: Microsoft's stock symbol Smart Tag at work (a second symbol is hidden right under the popup menu).
While this is very cool, I always wondered how much use it would be to me. First of all, I hardly ever receive documents or emails that have stock symbols in them. Secondly, even if they did, who says I wanted to use Microsoft's stock site (which is where the action items would take me). No, to me, this was not useful at all.
But, as Microsoft claimed, this was just an example. There were other things Word would recognize as well, such as dates and times. This would allow me to directly schedule meetings if someone sent me an email proposing one. That seemed to make more sense, although most of the meeting requests I get already utilize Outlook's (or Exchange's) meeting features. So all this Smart Tag stuff was kind of like supermodels: sexy, but of no direct use to me.
The technology itself, however, had me intrigued, especially the ability to build more useful Smart Tags myself. Think about it: what are the chances that Microsoft will embed SmartTags that directly make sense for your business? However, what if you could get Word, Excel or Outlook to recognize product names that are linked to your order fulfillment system? What if your users could place, fulfill and track customer orders right from within their email? You could provide a whole new user experience. There is little doubt in my mind that users would be more productive in this kind of environment.
Creating the Recognizer
So, let's go ahead and implement our own Smart Tag! Here is the basic concept: a developer can create Smart Tags by implementing a few simple Smart Tag interfaces. This means that you have to build a COM component containing certain methods and properties that can be called by Word and other Office XP applications.
You can use the Visual FoxPro 7 Object Browser (see Figure 2) to explore the Microsoft Smart Tags 1.0 Type Library. There are two interfaces that we will use in this example: ISmartTagRecognizer and ISmartTagAction. The first interface is used to find useful information within documents (strings). The ISmartTagAction interface is used to actually execute desired actions for a discovered phrase.
Figure 2: Visual FoxPro's Object Browser can be used to explore and implement the required Smart Tag interfaces.
To start implementing a Smart Tag, create a new PRG, and drag and drop both interfaces from the Object Browser into the VFP 7 source editor. This creates two classes that implement each of the interfaces. In my example, I chose to combine both interface implementations into one object, but whether you have two objects with one interface each, or one object that combines both, doesn't matter.
Listing 1 shows the code that implements our complete Smart Tag. Before we can do anything else, our Smart Tag recognizer engine needs to scan the text passed to it to find matches (product names, in our case). For this example, I used the products table that ships as a sample with Visual FoxPro 7. Whenever an Office XP application is ready for the recognizers to parse the available text, it will call the Recognize() function on the ISmartTagRecognizer interface.
The first parameter passed to this method is the actual text to be parsed. I'm using the FoxTools.fll and its Words() and WordNum() functions to search through the text word by word (see sidebar). I then try to locate records in the product database that contain items whose names start with the word I just identified (I check in two different fields). Note that you never know the length of the product name. Therefore, it's difficult to perform a search on the full product name. It would be much easier to search based on a product ID, but I think it is very unlikely that I'll receive an email that contains our internal IDs.
Whenever I find a match in the database, I take the entire product name (which I just identified as a possible match), and verify whether the entire name in fact does occur in the string. If so, we have a match and it's time to communicate that to the host application. We can do so through the RecognizerSite object that is also passed to the Recognize() method as a parameter (in my example, this parameter is called RecSite to keep the listing more readable in our narrow column width).
So, let's think about what we are trying to accomplish here. First of all, we would like to tell Word (or whatever application we are running), that we have found a string to which we can link some action. However, there is some information needed to perform that action that Word does not have?the product ID, for instance. The way to preserve this information is to use the "Property Bag." This is simply an object that can be used to store values, which can be retrieved later when needed (as would be the case if the user clicked on our Smart Tag). We can use the RecognizerSite to generate a new property bag for us. We then store the ItemID into the property bag by calling its Write() method.
OK, that's it! We can commit our Smart Tag to the host by calling the CommitSmartTag() method on the RecognizerSite object. The method requires four parameters: First, we need to pass a unique identifier, which has to conform to namespace rules. This makes sure the host application can identify the Smart Tag in case other recognizers are also in use. The second parameter is the start position of the identified word, and the third is the length. Finally, the fourth parameter is the property bag we just created.
Once the Smart Tag is committed to the host application, we scan the string word by word until we reach the end.
You are probably wondering why I went through all the trouble of traversing the string word by word. I could have also simply scanned the entire products table and compared each product name with the string I received. This would certainly work. However, if I had a very large product table, this would get very slow; since Office XP calls this method quite often, this would be a bad idea. By looking for individual words first, I eliminate the majority of records and speed up the process significantly.
You will notice that the ISmartTagRecognizer interface implements several properties, as well. Most of them are descriptive in nature and you can modify them at will. There are a few that are important, though: the ProgID has to be the class name of the COM component you are about to create. In my example, I am using a project file named "Order," and the class itself is called "Recognizer." Hence, the class name is "Order.Recognizer." The count is also important, because it identifies how many different kinds of Smart Tags the recognizer implements. In our example, there is only one. And finally, there is the Smart Tag name, which is the same unique name we pass to the CommitSmartTag() method. The two have to match or the host application will not be able to identify the smart tag when the user clicks it.
One other method that's fairly interesting is the download URL. Once a Smart Tag is used in a document, it can embed a URL pointing to the Smart Tag recognizer into the actual document. This way, you can provide the document to a user who doesn't have the recognizer. The document itself would always be functional, but the recognizer functionality would be gone. But if there is a URL embedded, the second user can simply download the custom recognizer.
Implementing the Associated Actions
At this point, we can find product information within a document, but we cannot perform any actions when the user clicks on our Smart Tag.
I intend to implement five different actions associated with products: first, show the actual item to see its details; second, place an order for the item; third, show all orders that are currently pending for the item; fourth, display sales statistics for the item; and fifth, forward product information to a customer.
Whenever a user clicks on a Smart Tag, the host application queries the number of associated actions using the VerbCount property. It passes along the Smart Tag name, just in case. Whenever our name shows up, we simply return the number 5.
Then, the host application queries the names and description of each item, using the VerbNameFromID and VerbCaptionFromID properties. The parameter passed along is the action item number for the name to be queried. We simply return the names of the action items (which usually end up as menu items on the Smart Tag).
This information is sufficient for the host application to build a Smart Tag menu. Once the user clicks one of the menu items, the host application runs the InvokeVerb() method on our recognizer and action object. Again, the VerbID is passed along and we use a simple case statement to execute the appropriate action. In my example, I use a Hyperlink object to navigate to our Web-based order entry system. Note that I'm using the PropertyBag (which is passed along to this method) to retrieve the ItemID (the first property in the bag) and add it to the URL string.
Of course, we could have performed all kinds of actions based on the capabilities of Visual FoxPro. However, make sure to call out to some other process (perhaps by instantiating another COM component or navigating to another application or Web page) to perform a lengthy process. In Visual FoxPro, there is no way to launch a custom thread within a process. Therefore, the Smart Tag Action object would be kept busy until your process finishes. The results of that might be unexpected: from the host application not responding until the action object is done, to a complete crash (I haven't seen this myself, but the Smart Tag SDK makes a big effort to point this out).
Note that you also have the opportunity to communicate with the host application at this point. The second parameter provides the name of the host application (so you can tailor your actions according to the host and send a string to Word or set a cell in Excel, for example). Parameter number three is a reference to the actual application.
Registering our Recognizer and Action Object
Once we compile our component, we are almost done. The only step left is to register the object so Office XP can find and use it. Once the Smart Tag is registered, Office XP will automatically load it and call it regularly whenever the contents of a document change.
To register our component, we need to find out the ClassID (guid) that has been generated for it. This is rather easy. Open the Registry (choose "Run" from the Start menu and type "regedit"). Then, drill down into "My Computer / HKEY_CLASSES_ROOT / Order.Recognizer / CLSID," and double-click the "(Default)" item. Copy the value (guid) into the Clipboard.
Now, we need to add this ClassID to the recognizer and action objects used by Office XP. To do this, drill down into the "My Computer / HKEY_CURRENT_USER / Software / Microsoft / Office / Common / Smart Tag" node. Within that node, you will find two sub-nodes called "Actions" and "Recognizers." Right-click each tag, select "New / Key" and paste the class id as the key name (see Figure 3). Voila!
Figure 3: We are adding our object's ClassID as recognizer and action objects to the registry.
Note: Make sure to shut down all Office XP applications, such as Word and Outlook, before you make these changes. Oh, by the way: I'm sure you know that changing any of the other stuff in the Registry can be troublesome? OK, just checking.
Figure 4: The Order Smart Tag in Action (Microsoft Word). Note the red dotted underline under product names "Tofu," "Pavlova" and "Mishi Kobe Beef."
Figure 5: Excel is also capable of utilizing Smart Tags. Unlike Word, however, Excel uses a little purple triangle in the corner of a cell to indicate that the text has been recognized.
Figure 6: Using Outlook, Smart Tags even work for email. In this example, we use it to track a customer order.
Now, let's go ahead and test our Smart Tag! Start up Word, and type some text that contains any of the product names found in the products table. If everything went fine, they should be recognized almost immediately. Figures 4, 5 and 6 show our Smart Tag in action.
Creating Smart Tags is not very difficult. Once you get used to the terminology used to implement COM interfaces, you will find it very straightforward. Microsoft has been criticized for adding too much default Smart Tag functionality. For this reason, much of the default Smart Tag functionality has been eliminated from Internet Explorer. Custom Smart Tags however, enable knowledge workers to be more productive in their everyday work environment.
Let me know what uses you come up with for Smart Tag technology, or drop me an email in case you have any questions!