Programmatically executing SmartTag action(s) in document... and in footnotes! It's HEAVYMETAL!
Recently I ran across interesting problem regarding Smart tags in VSTO. I have a document with some text and I created a smart tag which recognizes the text according to the regex values. Everything works fine; text that needs to be recognized is recognized. But I had some problems with footnotes ... I tried search the MSDN VSTO forum and there are only a couple of threads on executing smart tags (if you search for 'smart tags execute' you will get 4 threads and only one is relevant). Well, try searching for 'heavymetal' (click here) and you will get the relevant results :) Funny search string, well John R. Durant answered a similar question and he used that string in his example. You should check John's blog for great content on VSTO and Office in general.
Ok, now when you checked his blog, let's continue.
I will be using the example from MSDN to demonstrate the problems I had . Example smart tag will recognize short airport names and convert those names to the long version when user clicks on the recognized smart tag action.
Code is very simple, this is the code from ThisDocument_Startup:
1: Action airportAction;
2:
3: SmartTag airportSmartTag = new SmartTag("http://www.microsoft.com/VSTO#AirportSmartTag", "Airport"); 4:
5: // Add the simple terms for the Airport codes.
6: airportSmartTag.Terms.Add("SEA"); 7: airportSmartTag.Terms.Add("FRA"); 8: airportSmartTag.Terms.Add("STL"); 9: airportSmartTag.Terms.Add("HKG"); 10: airportSmartTag.Terms.Add("YYZ"); 11: airportSmartTag.Terms.Add("LAX"); 12:
13: // Create an instance of the airport action.
14: airportAction = new Action("Click me!"); 15:
16: // Add an event handler for the click event of the new action.
17: airportAction.Click += new ActionClickEventHandler(airportAction_Click);
18:
19: // Add the action to the Actions array on the smart tag.
20: airportSmartTag.Actions = new Action[] { airportAction }; 21:
22: // Add both smart tags to the VstoSmartTags collection.
23: this.VstoSmartTags.Add(airportSmartTag);
24:
25: // Force Word to check for smart tags when the document loads
26: this.RecheckSmartTags();
Next, here's the code from airportAction_Click:
1: string airportCode = e.Text;
2: string airportName = String.Empty;
3:
4: // Determine the full airport name based on the airport code.
5: switch (airportCode)
6: { 7: case "SEA":
8: { 9: airportName = "Seattle-Tacoma International Airport";
10: break;
11: }
12: ...
13: // etc. the code is similar for other short names
14: }
15: // replace short name with long name
16: e.Range.Text = airportName;
When you run the code everything works as expected. Even if you add some text to footnotes the code will recognize the text.
What I wanted to do next is to run the airportAction_Click programmatically for each recognized short name, so that the user doesn't have to click on each recognized text and execute the action.
Now, how can you do that? Well, you have a SmartTags collection and you could iterate through each item, check the name and then execute it.
Execute smart tag action for each item:
1: object oName = "http://www.microsoft.com/VSTO#AirportSmartTag";
2: object oFirst = 1;
3:
4: for (int i = 1; i <= this.InnerObject.SmartTags.Count; i++)
5: { 6: object index = i;
7:
8: Word.SmartTag st = this.SmartTags.get_Item(ref index);
9:
10: if (st.Name == oName.ToString())
11: { 12: this.SmartTags.get_Item(ref index).SmartTagActions.get_Item(ref oFirst).Execute();
13: }
14: }
In this piece of code we simply iterate through every smart tag in the document, check if the names match (so we now we are executing our smart tag action) and finally we execute it.
This should work, right? Well, it doesn't! There are 2 problems. For the first one there's a logical explanation. Check the method airportAction_Click (the last line). In last line we change the text (short name) to long name and each time we execute the action the value of InnerObject.SmartTags.Count gets decreased. That's logical because the document doesn't contain the text our smart tag has to recognize.
Ok, let's fix this. We will simply create a new variable (let's call it iCount and assign it the value of this.InnerObject.SmartTags.Count). If you change this, it will work fine. Every action will be executed, except the ones in footnotes. Weird! Looks like you will need to do another iteration and execute the actions for smart tags in footnotes separately.
Here is the code to execute actions from smart tags in footnotes:
1: int iCountInFootnotes = StoryRanges[Microsoft.Office.Interop.Word.WdStoryType.wdFootnotesStory].SmartTags.Count;
2:
3: for (int i = 1; i <= iCountInFootnotes; i++)
4: { 5: object index = i;
6: Word.SmartTag st = StoryRanges[Microsoft.Office.Interop.Word.WdStoryType.wdFootnotesStory].SmartTags.get_Item(ref index);
7:
8: if (st.Name == oName.ToString())
9: { 10: StoryRanges[Microsoft.Office.Interop.Word.WdStoryType.wdFootnotesStory].SmartTags.get_Item(ref index).SmartTagActions.get_Item(ref oFirst).Execute();
11: }
12: }
Code is similar as the one above, the only difference is that we get the SmartTags from StoryRanges.
And now you know how to execute all that smart tags in your footnotes :)