Creating Application-Level Smart Tags in VSTO

VSTO’s document-level Smart Tags are great when you want to recognize a term in a particular document or a class of document created from a template. What are your options when you want to recognize a term in all open documents?

I'm going to show two ways--the easy way and the hard way.  My first version of this post ignored the easy way--hence the repost.  The easy way is the way you will add a smart tag that you want to be active in all documents.  The hard way accomplishes the same thing, but is a little more flexible in that you can be selective about what documents have what smart tags--for example you could add a smart tag conditionally to a document based on its content or you could add one type of smart tag to certain documents and another smart tag to other documents.  I also want to show the hard way as well because it introduces GetVstoObject which is a way to access document level features at the add-in level.

The Easy Way

Starting with VSTO 3.0 SP1, you can create a VSTO add-in that can access its own VstoSmartTags collection. This now allows you to use VSTO Smart Tags at the add-in level. To use VSTO Smart Tags in an add-in, first make sure you have SP1 of VSTO 3.0 and Visual Studio 2008 installed. Also note that end-users of your solution must have SP1 of the VSTO 3.0 runtime installed.

Next, create a new Word or Excel add-in in Visual Studio 2008 SP1. New add-in projects created with Visual Studio 2008 SP1 have a VstoSmartTags collection as a member of the ThisAddIn class.

The code below shows a VSTO Word add-in that recognizes the term “Mackerel” and “Halibut” in all Word documents by using the new VstoSmartTags collection available to VSTO Word and Excel add-ins.

 using System;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Word;
using VSTO = Microsoft.Office.Tools.Word;
using Microsoft.Office.Tools.Word.Extensions;
using System.Windows.Forms;

namespace WordAddIn3
{
  public partial class ThisAddIn
  {
    SmartTag mySmartTag;
    VSTO.Action myAction;
    VSTO.Action myAction2;

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
      mySmartTag = new SmartTag(
        "https://vsto.aw.com#fish", "Fish Catcher");
      mySmartTag.Terms.Add("Mackerel");
      mySmartTag.Terms.Add("Halibut");

      myAction =
        new VSTO.Action("&Fishing///&Catch a fish...");
      myAction2 =
        new VSTO.Action("&Fishing///&Throw it back...");
      mySmartTag.Actions =
        new VSTO.Action[] { myAction, myAction2 };

      myAction.Click += new
        ActionClickEventHandler(myAction_Click);
      myAction2.Click += new
        ActionClickEventHandler(myAction2_Click);

      VstoSmartTags.Add(mySmartTag);
    }

    void myAction2_Click(object sender, ActionEventArgs e)
    {
      MessageBox.Show(String.Format(
        "You threw back a fish at position {0}.",
        e.Range.Start));
    }

    void myAction_Click(object sender, ActionEventArgs e)
    {
      MessageBox.Show(String.Format(
        "You caught a fish at position {0}.",
        e.Range.Start));
    }

    #region VSTO generated code
    private void InternalStartup()
    {
      this.Startup += new System.EventHandler(ThisAddIn_Startup);
    }
    #endregion
  }
}

The Hard Way

The "hard" approach accesses the VstoSmartTags collection associated with each document.  This approach is instructive because it will help you to understand the new GetVstoObject method which gives you access to document-level features at the add-in level.  You might actually use the hard way approach if you wanted to have an add-in that added different smart tags to different documents.

To use document level features in an add-in, first make sure you have SP1 of VSTO 3.0 and Visual Studio 2008 installed. Also note that end-users of your solution must have SP1 of the VSTO 3.0 runtime installed.

Next, either create a new add-in in Visual Studio 2008 SP1 or if you have an existing add-in, add the line “using Microsoft.Office.Tools.Word.Extensions” for a Word add-in and “using Microsoft.Office.Tools.Excel.Extensions” for an Excel add-in. To access the VstoSmartTags collection associated with a Word document or Excel workbook, use the GetVstoObject extension method that is available off of a Microsoft.Office.Interop.Word.Document object or Microsoft.Office.Interop.Excel.Workbook object.

The code below shows a VSTO Word add-in that recognizes the term “Mackerel” and “Halibut” in all open Word documents. In ThisAddIn_Startup, we iterate over any open documents and call our AttachSmartTag method. The AttachSmartTag method initializes a single SmartTag object that is held in the class variable mySmartTag and two Action objects: myAction and myAction2. These 3 objects can be used with multiple documents. Finally, the line of code below adds the mySmartTag object to the VstoSmartTags collection associated with the Word document object doc passed to AttachSmartTag.

 doc.GetVstoObject().VstoSmartTags.Add(mySmartTag);

The GetVstoObject extension method returns the Microsoft.Office.Tools.Word.Document object corresponding to the Microsoft.Office.Interop.Excel.Document object. The VstoSmartTags property is then used to get a VstoSmartTags collection and the Add method of the collection is called to add the smart tag .

After the ThisAddIn_Startup method iterates over currently open documents to attach smart tags, the add-in handles the DocumentOpen and NewDocument events so that when new documents are created or documents are opened a smart tag is added to the VstoSmartTags collection associated with these documents as well.

 using System;
 using Word = Microsoft.Office.Interop.Word;
 using Office = Microsoft.Office.Core;
 using Microsoft.Office.Tools.Word;
 using VSTO = Microsoft.Office.Tools.Word;
 using Microsoft.Office.Tools.Word.Extensions;
 using System.Windows.Forms;
  
 namespace WordAddIn2
 {
   public partial class ThisAddIn
   {
     SmartTag mySmartTag;
     VSTO.Action myAction;
     VSTO.Action myAction2;
  
     private void ThisAddIn_Startup(object sender, 
       System.EventArgs e)
     {
       foreach (Word.Document doc in Application.Documents)
       {
         AttachSmartTag(doc);
       }
  
       this.Application.DocumentOpen += new 
         Word.ApplicationEvents4_DocumentOpenEventHandler(
         Application_DocumentOpen);
       ((Word.ApplicationEvents4_Event)Application).NewDocument +=
         new Word.ApplicationEvents4_NewDocumentEventHandler(
         Application_NewDocument);
     }
  
     void Application_NewDocument(Word.Document doc)
     {
       AttachSmartTag(doc);
     }
  
     void Application_DocumentOpen(Word.Document doc)
     {
       AttachSmartTag(doc);
     }
  
     void AttachSmartTag(Word.Document doc)
     {
       if (mySmartTag == null)
       {
         mySmartTag = new SmartTag(
           "https://vsto.aw.com#fish", "Fish Catcher");
         mySmartTag.Terms.Add("Mackerel");
         mySmartTag.Terms.Add("Halibut");
  
         myAction =
           new VSTO.Action("&Fishing///&Catch a fish...");
         myAction2 =
           new VSTO.Action("&Fishing///&Throw it back...");
         mySmartTag.Actions =
           new VSTO.Action[] { myAction, myAction2 };
  
         myAction.Click += new 
           ActionClickEventHandler(myAction_Click);
         myAction2.Click += new 
           ActionClickEventHandler(myAction2_Click);
       }
       doc.GetVstoObject().VstoSmartTags.Add(mySmartTag);
     }
  
     void myAction2_Click(object sender, ActionEventArgs e)
     {
       MessageBox.Show(String.Format(
         "You threw back a fish at position {0}.",
         e.Range.Start));
     }
  
     void myAction_Click(object sender, ActionEventArgs e)
     {
       MessageBox.Show(String.Format(
         "You caught a fish at position {0}.",
         e.Range.Start));
     }
  
     #region VSTO generated code
     private void InternalStartup()
     {
       this.Startup += new System.EventHandler(ThisAddIn_Startup);
     }
     #endregion
   }
 }