RibbonX for Dummies: Chapter 6 (Part 2) (
Page 4 of 6 )
Creating the Templates variable and interacting with Word
Many of the callbacks rely on a global variable named Templates that contains
a list of the templates for the current user. The example places the code
to create this variable in the OnLoad() callback. Here's the code that makes
everything else work:
// Obtain the user's path to templates.
string TemplatePath =
Globals.ThisAddIn.GetTemplatePath();
// Build a list of form templates.
Templates = Directory.GetFiles(
TemplatePath + @"\Forms", "*.DOTX");
The code begins by obtaining the location of the templates for the current
user. You could use any other location on the hard drive as a starting point,
but using the current user's folder ensures that Word can find the templates
it needs. All of the forms reside in a special folder named Forms and have a
DOTX file extension, so using the GetFiles() method makes it easy to fill
the Templates array with a list of template filenames. Of course, the big
question is where the Globals.ThisAddIn.GetTemplatePath() method
resides.
ADVERTISEMENT
You can't interact directly with Word from the Ribbon1.CS file; all you can
do is interact with the Ribbon. Consequently, if you want the add-in to perform
any useful work, you must call on public methods in the ThisAddIn
class located in ThisAddIn.CS. Unfortunately, you can't access this class
directly. If you try to create an instance of the class or create static members,
you quickly find that you can't achieve any results, even if the code compiles.
You always access the members of the ThisAddIn class using the Globals
object as shown. When you type Globals, instantly you see a list of add-ins;
after selecting a list of add-ins, you'll see the public members for that add-in.
Here, the reason for the special emphasis is that Microsoft isn't particularly
good about documenting these interactions — you almost need to know
already that they exist, without Microsoft's help. The GetTemplatePath()
method simply accesses Word and obtains the current path, as shown here:
public String GetTemplatePath()
{
// Obtain the template path for Word and return it.
return Application.NormalTemplate.Path;
}
Obtaining the template information for display
The remaining attributes define callbacks that Word needs to define the
gallery content. Each image requires the getItemID and getItemImage
attributes as a minimum (the getItemLabel attribute is optional, as are
many of the other callbacks). The code must also implement the
getItemCount attribute or the gallery won't know how many templates to
display. Finally, you need to implement onAction to provide some means of
reacting to user selections. Listing 6-12 shows the code required to implement
the callbacks for this part of the example.
Listing 6-12: Providing the Gallery Items
public long GetItemCount(Office.IRibbonControl control)
{
// Return the number of items based on the template
// length.
return Templates.Length;
}
public string GetItemID(Office.IRibbonControl control,
int index)
{
// Obtain the template name.
string ThisID =
Path.GetFileNameWithoutExtension(
Templates[index]);
// Remove any extra spaces.
ThisID = ThisID.Replace(" ", "");
// Return the item id based on the template name.
return ThisID;
}
public Bitmap GetItemImage(
Office.IRibbonControl control, int index)
{
// Obtain a pointer to the current template image.
String TemplateLocation = Templates[index];
TemplateLocation =
TemplateLocation.Replace(".dotx", ".png");
// Create a bitmap based on the image.
Bitmap TemplateImage = new Bitmap(TemplateLocation);
// Return the bitmap.
return TemplateImage;
}
public string GetItemLabel(
Office.IRibbonControl control, int index)
{
// Return the template name.
return
Path.GetFileNameWithoutExtension(
Templates[index]);
}
public void ItemClicked(Office.IRibbonControl control,
string selectedId,
int selectedIndex)
{
// Use this path to supply information for the
// template.
Globals.ThisAddIn.CreateNewDocument(
Templates[selectedIndex]);
}
The GetItemCount() method simply returns the length of a special variable
named Templates that contains an array of template path strings. For right
now, all you need to know is that the array shows where each template in the
list resides. The "Creating the Templates variable and interacting with Word"
section of the chapter describes how the application creates this variable.
The Templates.Length property tells how many items the array contains,
which therefore tells you how many elements the gallery has.
Every item in the gallery must have a unique ID. The GetItemID() method
provides a unique ID based on the template filename. It's unlikely that two
templates will have a name variation so close that it results in an ID collision.
Notice how the code uses the Path.GetFileNameWithoutExtension()
method to obtain just the filename, and then takes the spaces out of the filename
to produce the ID.
Using the filename approach to creating IDs also makes it easier to debug
your application. If a particular template causes problems, you can discover
its name quickly and perform any required changes.
Remember that every one of the templates has an associated .PNG file that
contains the screenshot of that template. The GetItemImage() method
uses this feature to obtain an image for the gallery. The code begins by
obtaining a template location, and then replaces the template file extension
with the PNG extension. The code can then create a Bitmap for the image
and return it to Word. Notice how none of the resources in this example
require an absolute directory location — the code automatically compensates
for differences in system setup.
The templates for this example have descriptive names; that way they're
easier to locate if they require changes. In fact, there's no reason not to give
them descriptive names. The GetItemLabel() method uses this template
feature to create the labels for each gallery item. You can't count on the user
seeing enough of the template to make a choice, so the distinctive name is a
requirement to ensure the user makes the right selection at the outset.
The ItemClicked() method provides that last piece of the puzzle. When a
user clicks on one of the templates, Word calls this callback with the selected
item's ID and index. The code makes a call to the Globals.ThisAddIn.
CreateNewDocument() method to actually create a new form, basing the
form on the template location provided in the Templates variable. You'll
always need to add this extra code because Office doesn't let you access the
application directly through the Ribbon callback code.
Creating the new document with a particular template
In many respects, working with Word in Visual Studio looks like merely a different
kind of VBA application. Of course, there are differences, but the essential
coding tasks are the same. Listing 6-13 shows the code required to create
a new form based on one of the templates you created earlier in this example.
Listing 6-13: Creating the new form based on the selected template
public void CreateNewDocument(string ThisTemplate)
{
// Get rid of Document1 if the user has recently
// opened Word and this is the first document
// created.
if (Application.ActiveDocument.Name == "Document1")
// Make sure the document is visible so the user can
// see it.
object objVisible = (object)true;
// Create the document.
Application.Documents.Add(ref objTemplate,
ref objNewTemplate,
ref objDocumentType,
ref objVisible);
}
The code begins by checking for Document1, the default name of the document
that appears when you open Word. Notice that the code makes this
check without creating any special objects. If you try to type Application
and don't see the IntelliSense for the relation objects, you're probably working
in the wrong file.
The code begins by creating some variables for the Application.Active
Document.Close() method. Unlike VBA, this code won't let you simply skip
arguments. You must provide every required argument for the method calls,
even if that means supplying a default value.
Most of the calls you'll make refer to the object class — they don't provide
much in the way of information about the kind of input the call expects. The
example doesn't take any chances with the user's data. If you have any doubt
about the data type, look up the method call in the VBA documentation.
Notice how the example uses type coercion to document the values for each
argument. The code uses the actual VBA types, but then coerces them to the
object type. VB.NET users will find that they need to do less work than C#
developers because VB.NET performs some of the type coercion for you.
The example calls the Application.ActiveDocument.Close() method
using the ref keyword in C#. VB.NET developers don't have to add the extra
keyword. The reason you must include this keyword in C# is to allow Word to
pass back information (it happens rarely — make sure you understand how
the method call works before you assume that Word will provide any return
values). Notice that the example uses Word.WdSaveOptions.wdPromptTo
SaveChanges to ensure that the user doesn't lose any data. Word won't display
a dialog box unless the user has made changes to the document.
Creating the new document comes next. As with the Application.
ActiveDocument.Close() method, the Application.Documents.
Add() method requires not only that you pass arguments of the object
type, but also that you pass them by reference. The objTemplate argument
always contains the name of the template you want to create. The remaining
arguments define how Word creates the document. You can choose to create
the document as a template, to use a document type other than a blank document
(such as an e-mail), and even to create invisible documents so you can
work in the background.