Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional VSTO 2005 - Visual Studio 2005 Tools For Office (2006) [eng]

.pdf
Скачиваний:
121
Добавлен:
16.08.2013
Размер:
8.04 Mб
Скачать

Chapter 4

class Program

Shared Sub Main(ByVal args() As String) Dim automator As Object

Try

automator = Marshal.GetActiveObject(“Word.Application”) Dim app As Word.Application = automator as Word.Application app.ShowClipboard()

Catch

‘Microsoft Word is not running so no automation can occur Finally

Marshal.ReleaseComObject(automator) End Try

End Sub End namespace

C#

using System;

using System.Runtime.InteropServices; using Word = Microsoft.Office.Interop.Word; using Office = Microsoft.Office.Core;

namespace ConsoleAutomation

{

class Program

{

static void Main(string[] args)

{

try

{

object automator = Marshal.GetActiveObject(“Word.Application”); Word.Application app = automator as Word.Application; app.ShowClipboard();

}

catch (System.Runtime.InteropServices.COMException)

{

//Microsoft Word is not running so no automation can occur

}

finally

{

Marshal.ReleaseComObject(automator);

}

}

}

Listing 4-18: Sample late binding code

The console application is a stand-alone application that runs outside of the Office application. Notice how the code creates an instance of the appropriate Office Word object. Once this is accomplished, all the functionality of Word can be leveraged through the reference to the object. In our instance, we simply show the clipboard in the taskbar.

138

Word Automation

Listing 4-22 contains exception handling for the case where the automation is attempted and an instance of Microsoft Word is not running. In that case, you can either exit with an appropriate message or attempt to start an instance of Microsoft word. The code calls the ReleaseComObject method to ensure that resources are cleaned up correctly. This call is necessary because the example illustrates regular COM Interop.

A Smart Tag Example

If you have worked with Office XP and subsequent versions for a while, you will notice that Microsoft Word tags some words in the document. When you hover over these words, an icon appears containing a drop-down menu that enables more functionality associated with these particular words. Figure 4-9 shows smart tagging in action.

Documents that support smart tags must have that feature that enables for smart tagging to function correctly. In an Office Word application, turn on the feature by selecting Tools AutoCorrect Options, select the Smart Tags tab, and click the “Label text with smart tags” checkbox, as shown in Figure 4-11. You may want to consider turning this feature off if you are not particularly impressed with the smart tag functionality.

Figure 4-9

139

Chapter 4

There are a couple of ways to create smart tags in Office documents. One approach creates tags at design time using XML, the other creates smart tags at runtime. Let’s consider the design-time approach first. After examining the XML fragment shown in Listing 4-19, copy it into Notepad or download the sample from the book’s website at www.wrox.com. Save the file as Books.xml in the default location that Office uses to load smart tag functionality: C:\Program Files\Common Files\Microsoft Shared\Smart Tags\Lists. The action creates a Microsoft Office Smart Tag List (MOSTL) file that defines the behavior of smart tags in the active document.

<FL:smarttaglist xmlns:FL=”urn:schemas-microsoft-com:smarttags:list”> <FL:name>Book Index</FL:name>

<FL:lcid>1033,0</FL:lcid> <FL:description>Available Books</FL:description> <FL:updateable>false</FL:updateable> <FL:updatefrequency>10080</FL:updatefrequency> <FL:autoupdate>true</FL:autoupdate>

<FL:smarttag type=”urn:schemas-microsoft-com:office:smarttags#BooksIndex”> <FL:caption>Book Indices</FL:caption>

<FL:terms>

<FL:termlist>VSTO,OWC,.NET</FL:termlist>

</FL:terms>

<FL:actions> <FL:action id=”BookId”>

<FL:caption>Further Reading</FL:caption> <FL:url>http://{TEXT}.amazon.com</FL:url> </FL:action>

</FL:actions>

</FL:smarttag>

</FL:smarttaglist>

Listing 4-19: Sample XML file

In order for the smart tag to work inWord, you must save the file with an XML extension in the smart tag directory given above. Then, select Tools AutoCorrect Options. Click the Smart tags tab. Finally, click the Recheck Document button and accept the confirmation dialog that pops up. Figure 4-10 shows the functionality in action based on our XML file. You may want to consider using design-time smart tagging as the opportunities present themselves.

In some cases, design-time smart tagging may not be feasible so let’s consider a runtime implementation. Our example simply implements the first approach of Listing 4-14. Listing 4-20 has the code.

Visual Basic

Private Sub ThisDocument_Startup(ByVal sender As Object, ByVal e As System.EventArgs)

Dim myTag As WordTools.SmartTag = New WordTools.SmartTag(“http://www.lulu.com/owc#OWC”, “OWC”)

myTag.Terms.Add(“OWC”)

myTag.Terms.Add(“VSTO”)

myTag.Terms.Add(“.NET”)

Dim act As WordTools.Action = New WordTools.Action(“B&ooks///&Go”) AddHandler act.Click, AddressOf act_Click

myTag.Actions = New WordTools.Action() {act} VstoSmartTags.Add(myTag)

140

Word Automation

C#

private void ThisDocument_Startup(object sender, System.EventArgs e)

{

WordTools.SmartTag myTag = new WordTools.SmartTag(“http://www.lulu.com/owc#OWC”, “OWC”);

myTag.Terms.Add(“OWC”);

myTag.Terms.Add(“VSTO”);

myTag.Terms.Add(“.NET”);

WordTools.Action act = new WordTools.Action(“B&ooks///&Go”); act.Click += new

Microsoft.Office.Tools.Word.ActionClickEventHandler(act_Click); myTag.Actions = new WordTools.Action[] { act }; VstoSmartTags.Add(myTag);

}

Listing 4-20: Showing runtime smart tag implementation

Figure 4-10

141

Chapter 4

The code is fairly simple. A new smart tag object is created that points to a particular URI. Notice that the URI must contain two parts separated by the # character otherwise a runtime exception is thrown. The terms are then added to the smart tag object collection so that the smart tag feature can recognize the specified text in the document proper and take appropriate action. That’s really all there is to smart tagging.

You can see that it is easy to get creative with that type of functionality because it is possible to hook up events to the tag click event, as Listing 4-20 shows. That option is not available in the XML approach, so you may consider using the runtime approach when smart tags need to be linked with complex resources or when some action or event needs to occur.

Another option is to download and install the Microsoft Smart Tag SDK from the Microsoft Office website. The SDK comes complete with sample implementation and quick start guide. For this reason, an example is not provided here. Be aware though that the SDK creates smart tags through COM Interop. VSTO has support for smart tags built in so you should prefer the VSTO approach to the SDK to avoid the overhead of COM Interop.

If you care to explore smart tags in more detail, you will find that the technology also supports regular expression filtering. Regular expressions enable complex use cases and are well outside the scope of this book. A good resource for regular expressions is www.regexlib.com. Figure 4-11 shows part of the time.xml file that is available with Microsoft Office 2003. The time.xml MOSTL contains its fair share of regular expressions for parsing time tags in an office document.

Figure 4-11

142

Word Automation

Manipulating Office Controls

You will find several examples of manipulating controls in VSTO in this book. More so, the code listed for control manipulation in the Excel chapters is applicable here as well. However, there are a few interesting controls that have surfaced in VSTO that deserve some special attention, so let’s consider two

of these.

ActionsPane controls and backgroundWorker controls are new for Visual Studio Tool for Office System. The backgroundWorker control may be added to the form from the toolbox. The control is designed to perform long-running tasks in the background. One reason for this control is that it allows the user interface to remain responsive while a resource intensive task is being performed. Symptoms of these long-running tasks usually involve a screen that appears to be frozen, unresponsive, or improperly painted during application execution.

Consider the conceptually simple example in Listing 4-21, which performs a long-running task.

Visual Basic

Private Sub ThisDocument_Startup(ByVal sender As Object, ByVal e As System.EventArgs)

backgroundWorker1.runWorkerAsync() End Sub

Private Sub ThisDocument_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs)

End Sub

Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)

System.Threading.Thread.Sleep(2400) End Sub

C#

Private void ThisDocument_Startup(object sender, System.EventArgs e)

{

backgroundWorker1.runWorkerAsync();

}

Private void ThisDocument_Shutdown(object sender, System.EventArgs e)

{

}

Private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)

{

System.Threading.Thread.Sleep(2400);

}

Listing 4-21: Long-running task execution

This example assumes that a backgroundWorker control has been dropped onto the document. At the start of the application, the backgroundWorker control starts the long running task with a call to runWorkerAsync method. This call fires the DoWork event handler where the code simulates a long-

running task. At this point, there are two separate paths of execution. The first execution path represents the main application, and the other represents the long running task.

143

Chapter 4

The laws that govern the way a backgroundWorker control operates with the main application are certainly not trivial and many books have been written about this complex relationship. However, there isn’t space in this book to discuss this topic. Certainly, the introduction of this control necessarily trivializes the relationship to the point that it may introduce instability to an otherwise well-written application.

Let’s proceed with an example to illustrate our point, in Listing 4-22. Hopefully, it will make things clearer.

Visual Basic

Imports System

Imports System.Data

Imports System.Drawing

Imports System.Windows.Forms

Imports Microsoft.VisualStudio.Tools.Applications.Runtime

Imports Word = Microsoft.Office.Interop.Word

Imports Office = Microsoft.Office.Core

Namespace WordActionsPane

Public partial Class ThisDocument

Private

Sub New_Startup(ByVal sender As Object, ByVal e As System.EventArgs)

backgroundWorker1.RunWorkerAsync()

End Sub

 

 

Private

Sub New_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs)

End Sub

 

 

Private

Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As

System.ComponentModel.DoWorkEventArgs)

Dim i As Integer

 

For i = 1 To 10 Step

1

System.Threading.Thread.Sleep(500) this.ActionsPane.Visible = Not this.ActionsPane.Visible Next

End Sub

End Class End Namespace

C#

using System; using System.Data;

using System.Drawing; using System.Windows.Forms;

using Microsoft.VisualStudio.Tools.Applications.Runtime; using Word = Microsoft.Office.Interop.Word;

using Office = Microsoft.Office.Core; namespace WordActionsPane

{

public partial class ThisDocument

{

private void ThisDocument_Startup(object sender, System.EventArgs e)

{

backgroundWorker1.RunWorkerAsync();

}

private void ThisDocument_Shutdown(object sender, System.EventArgs e)

{

}

144

Word Automation

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)

{

for (int i = 1; i < 10; i++)

{

System.Threading.Thread.Sleep(500); this.ActionsPane.Visible = ! this.ActionsPane.Visible;

}

}

}

}

Listing 4-22: Background control threaded interaction

Listing 4-22 includes a backgroundWorker control on a form. The method DoWork is responsible for executing the long-running task. In the example, the long-running task is a loop simulation that causes the application to sleep for 500 milliseconds each time the loop executes. On each pass of the loop, the application will toggle the ActionsPane visibility. The visibility property does not actually hide the actions pane, it merely hides the controls that are embedded in the ActionsPane. However, this activity is just what is needed.

If you execute the code in Listing 4-22, before long you should receive a runtime exception, indicating that a background thread cannot modify a UI component. But then again, on some Windows platforms, the code may actually execute without issue. In both cases, the outcome is disastrous albeit more so in the latter case, since this bug can lie undiscovered until the code is deployed in production.

The backgroundWorker control is actually a wrapper for a separate thread of execution — a miniprogram or task of sorts — that is allowed to execute while the main application is running. In essence, there are two paths of execution, one for the main application that fires the Startup routine and another path that fires the long-running task through the backgroundWorker control.

The long-running task, or child task, is forbidden to manipulate any UI object belonging to the parent task. In Listing 4-22, the UI component is the ActionsPane, and it belongs to the parent task. Had we used a button, text box, or any Windows control, the result would be the same because these Windows controls are owned by the parent task.

If the child task needs to modify a UI object owned by the parent task, it must formerly ask the parent task to do the modification for it. Consider the example of this formal request in Listing 4-23.

Visual Basic

Imports System

Imports System.Data

Imports System.Drawing

Imports System.Windows.Forms

Imports Microsoft.VisualStudio.Tools.Applications.Runtime

Imports Word = Microsoft.Office.Interop.Word

Imports Office = Microsoft.Office.Core Namespace WordActionsPane

Public partial Class ThisDocument

Private Sub New_Startup(ByVal sender As Object, ByVal e As System.EventArgs) backgroundWorker1.RunWorkerAsync()

End Sub

145

Chapter 4

Private

Sub New_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs)

End Sub

 

Private

Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As

System.ComponentModel.DoWorkEventArgs)

Dim i As Integer

For

i = 1 To 10- 1 Step i + 1

 

System.Threading.Thread.Sleep(500)

 

If(ActionsPane.InvokeRequired = true) then

ActionsPane.Invoke(new MethodInvoker(AddressOf ActionsPane.Hide()))

Else

ActionsPane.Hide()

EndIf

Next

End Sub

End Class

End Namespace

C#

using System; using System.Data;

using System.Drawing; using System.Windows.Forms;

using Microsoft.VisualStudio.Tools.Applications.Runtime; using Word = Microsoft.Office.Interop.Word;

using Office = Microsoft.Office.Core; namespace WordActionsPane

{

public partial class ThisDocument

{

private void ThisDocument_Startup(object sender, System.EventArgs e)

{

backgroundWorker1.RunWorkerAsync();

}

private void ThisDocument_Shutdown(object sender, System.EventArgs e)

{

}

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)

{

for (int i = 1; i < 10; i++)

{

System.Threading.Thread.Sleep(500);

If(ActionsPane.InvokeRequired)

ActionsPane.Invoke(new MethodInvoker(ActionsPane.Hide())); Else

ActionsPane.Hide();

}

}

}

}

Listing 4-23: UI manipulation by background controls

146

Word Automation

In Listing 4-26 you can see that the child task (long-running task) explicitly asks the parent task, who is the owner of the control, to do some work on its behalf. And the work is done without triggering an exception. The request is handled using the Invoke line of code. That sort of formal communication is the only way a child task can manipulate a control on the parent task. Remember, you shouldn’t expect the code to actually hide the actions pane control. The hide method call hides the controls embedded in the actions pane. To be certain, there are many other rules that come into effect with background controls so it is worthwhile to spend some time poring through MSDN and other resources to come up to speed on thread technology.

Word Event Model

The OfficeCodeBehind class containing the code behind the Office document model defines at least two events for the Word object. The ThisDocument_Open fires when either a document or a template opens. The thisDocument_Close fires when either a document or a template closes. You may use both these events to perform document initialization or cleanup as appropriate.

VSTO makes it easy to wire up additional events in your applications. Listing 4-24 is an example of this.

Visual Basic

Imports System

Imports System.Data

Imports System.Drawing

Imports System.Windows.Forms

Imports Microsoft.VisualStudio.Tools.Applications.Runtime

Imports Word = Microsoft.Office.Interop.Word

Imports Office = Microsoft.Office.Core

Namespace WordEvnts

Public partial Class ThisDocument

private void void New_Startup(Object sender, System.EventArgs e) End Sub

private void void New_Shutdown(Object sender, System.EventArgs e) End Sub

#region VSTO Designer generated code private void InternalStartup()

Me.Startup += New System.EventHandler(ThisDocument_Startup) Me.Shutdown += New System.EventHandler(ThisDocument_Shutdown) Me.BeforeClose += New System.ComponentModel.CancelEventHandler(ThisDocument_BeforeClose)

End Sub

void void New_BeforeClose(Object sender, System.ComponentModel.CancelEventArgs e) Throw New Exception(“The method or operation is not implemented.”)

End Sub #End Region End Class

End Namespace

C#

using System; using System.Data;

using System.Drawing;

147