Changing a Word Mail Merge Field in C#

The mail merge fields in Microsoft Word are actually just a specific type of the more general Fields. Fields can be used to make fill-in forms, autogenerated text, dates, etc… They typically are displayed surrounded by funny looking square brackets, like <<first_name>>.

Inside the Word Xml, the Field and the display value are stored separately – the field looks like “ MERGEFIELD User_Title “, then in a separate range, the display of “«User_Title»” is stored.

Using the Word Object Model, Document.MailMerge.Fields contains MailMergeField objects for each instance of a merge field. The .Code member contains the range object, of which the .Text member will contain the field code (aka “ MERGEFIELD User_Title “). If you do a replace on this text field, it will update the merge field, but the display will remain the same.

You next have to call Document.Fields.Update() to get the display matching.

Here is some code:

        private void RenameMergeField(Word.Document doc, string oldName, string newName)
        {
            foreach (Word.MailMergeField field in doc.MailMerge.Fields)
            {
                if (field.Code.Text.Contains(oldName))
                {
                    field.Code.Text = field.Code.Text.Replace(oldName, newName);
                }
            }
            doc.Fields.Update();
        }

Undocumented behavior of Office 2010 Word Save As dialog with Sharepoint

Here’s another Office 2010 undocumented “feature”.

First a little background regarding required fields in sharepoint:
In Office 2007, in order to attach the Sharepoint properties to a document you had to first Save it into the document library. Word would allow you to save, but the document would remain checked-out to you until you filled in the necessary required properties, and checked it in.

If you were developing a system that automated adding documents to SharePoint, you could not even present the Document Information Panel to the user until the document was already in SharePoint. If the user decided to cancel… you had hanging files.

With Office 2010, Word (and I imagine other apps) will not actually complete the Save operation to a SharePoint Document Library when there are required fields. You get a warning and the document is _not_ placed in SharePoint, but the Fields from the Document Library are attached to your Word document.

In Word, if you want to display the “Save As” dialog box pre-populated to a SharePoint library you do something like this:

dynamic dlg = Application.Dialogs[Word.WdWordDialog.wdDialogFileSaveAs];
dlg.Name = "http://sharepointserver/doclib/filename.docx";
dlg.Show();

This will act the same as the normal “Save As” dialog.

If you want to handle the save process yourself, instead of dlg.Show(), use dlg.Display. This *should* take no action regardless if the user presses Ok or Cancel. If the destination happens to be SharePoint – intsead of doing nothing like it is supposed to – Word happily retreives the Sharepoint properties and attaches them to your document.

In addition to being handy, Word will attach the document properties to Read-Only and Protected files – which is something you cannot do using CustomXMLParts.
And it’s a heck of a lot easier than trying to build and attach them yourself.

While it is undocumented (and therefore subject to change), it is kind of impossible to work around — Word WILL attach the sharepoint properties, so you might as well take advantage of it.

UPDATE 3/20/2012: Unfortunately, I have discovered that this method is unreliable. On one of our user’s machines, the Save As dialog refuses to display the sharepoint folder at all. I don’t know why, but I wouldn’t recommend relying on this.

Viewing and editing Sharepoint properties on a Word document… again

I have posted before about a method of editing the SharePoint Document properties on a Word document in Office 2010 (and Office 2007). I just found something that completely blew my mind. I spent hours and hours battling with CustomXMLParts, Protected and Readonly documents, and a Save-As dialog… and found out that all that effort was pretty much wasted.

Behold:
_Document.ContentTypeProperties Property
Microsoft.Office.Interop.Word.Document.ContentTypeProperties

ContentTypeProperties is a collection of MetaProperty objects representing (possibly among other things) the SharePoint properties displayed in the server document properties panel. And you can edit them.

So instead of a mess of XML xpath, namespaces, exception handling and “_x0020_”‘s , if you want to edit a sharepoint list column called “Task Id”, you would do the following:

doc.ContentTypeProperties["Task Id"].Value = "task1";

(Yes, you still might need some exception handling…)

The CustomXMLParts will be updated appropriately, and when you save it to sharepoint, the properties will be populated.

Have a look at the members of the MetaProperty object:
.Name = sharepoint internal column name: “Task Id”
.Id = escaped column name: “Task_x0020_Id”
The other properties are what you would expect.

Please note that this will only work if the word document has been “touched” by SharePoint. See my next post for a quick (and undocumented) method of getting those properties attached in Word 2010.

Office 2010 Backstage OnShow callback contextObject

The signature for the Office 2010 backstage OnShow callback looks like so:

void Backstage_OnShow(object contextObject)

But I cannot find anywhere that documents what the contextObject is.

Well I can tell you – it’s the Window object that is hosting the backstage as shown. I’m guessing it’s an object because it will be the equivalent Window object in whichever Office application happens to be running.

Undocumented Office Ribbon Callback functionality

I just stumbled on an undocumented feature of the getImage callback for the Office 2010 Ribbon (well, backstage actually)

The “official” callback signature is

Public System.Drawing.Bitmap GetImage(Office.IRibbonControl control) {}

Or sometimes

Public stdole.IPictureDisp GetImage(Office.IRibbonControl control) {}

I don’t know which is “best”, but if you return a Bitmap it sure is easier to work with.

Now back in Office 2007 there are a couple controls with a GetImageMso callback that allows you to provide an Office Control ID for a built-in image. This is lacking from any 2010 backstage controls.

Whilst doing some unpleasant native debugging to try to “figure out” what office expects in these callback delegates, I tried creating a GetImage callback that returned a string.

<?xml version="1.0" encoding="utf-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
  <backstage>
    <tab id="MyTabShare" label="Testing">
      <firstColumn>
        <group id="MyGroup">
          <topItems>
            <imageControl id="imageControl1" getImage="GetImage"/>
          </topItems>
        </group>
      </firstColumn>
    </tab>
  </backstage>
</customUI>
        public string GetImage(Office.IRibbonControl control)
        {
            return "hello world";
        }

I was really hoping for an error about a bad callback signature, possible including an exception in MSO.DLL that I could dig into…

Instead, I got this:

Really… So when GetImage returns a string, Office interprets it as an ImageMso? I had to try:

        public string GetImage(Office.IRibbonControl control)
        {
            return "WindowKeepOnTop";
        }

The result: SUCCESS!

So, at least for the Office 2010 backstage ribbon, the getImage callback has an alternate signature:

string GetImage(IRibbonControl control)

Where string is an Office Image Id (or the value you would place statically in imageMso).

Definitive complete list of Office Ribbon Callback Signatures for 2007 and 2010

Microsoft’s documentation for programming the Office Ribbon is pretty sparse at best. There are two pages you can get Callback signatures:
1. Customizing the 2007 Office Fluent Ribbon for Developers (Part 3 of 3)
2. Introduction to the Office 2010 Backstage View for Developers

Notice that neither of these documents sound like where you would expect callback signatures “part 3 of 3”, and “introduction to the…” Also, the backstage introduction article contains rehashes of most of the office 2007 callbacks in a painfully verbose fashion.

I am here to help.
How about an excel spreadsheet containing the callbacks, associated control, source (2007 vs 2010) and the signatures in 4 different languages? Did I mention the data is already in a table and sorted by callback name?

Here you go:
Full Office Ribbon Callback List
Update: I have uploaded this file to a public SkyDrive so it can be viewed online, since I have needed to make some changes:
Office Ribbon Callbacks

This document is not a control -> callback mapping. The typical use case is:
I am building Office customizations using xml.
I need to implement X callback, but do not know the signature.
I look up the callback by name, then make sure it is the correct one for my control.

Also, I didn’t quite finish reconciling some discrepencies between the 2010 and 2007 documentation – for example, in 2007 the getImage is supposed to return an IPictureDisp – which is a real pain in the butt. My experience showed that returning a bitmap worked anyway, and now the 2010 documentation says getImage returns System.Drawing.Bitmap. I have switched to using Bitmap because it is so much cleaner, but I figured I should leave them both in there.

I am open to suggestions – just trying to fill what I consider a documentation gap.

Changing the value of an editBox office Ribbon Control at runtime

When creating VSTO Add-Ins that customize the Office Ribbon, the visual Ribbon editor makes some tasks trivial. All Ribbbon controls can be accessed by id using intellisense. The RibbonEditBox.Text field can be directly queried and modified.
Everything changes when you create the RibbonXML directly.

You cannot directly query any of the runtime values on a ribbon control. You must use the OnChange callback to get the value in the box.

        public void OnChange(Office.IRibbonControl control, string text)
        {
        }

But what if you wanted to change the value of the editBox (aka change the text)? Say perform some validation, or formatting?

I tried casting the IRibbonControl object supplied by the callback to a RibbonEditBox object… This didn’t work.
I tried casting the control object as a Dynamic and accessing the “Text” property… No dice.
I looked for some means of getting the ribbon control so I could modify the Text value – nothing.

The solution:
(Which was surprisingly difficult to find)
There are three things you will need to do:
1. Within your ribbon’s class, make sure you are saving the IRibbonUI reference provided by Ribbon_Load to a private variable. I just call it “ribbon”
2. Implement the getText callback and a private string variable to hold the internal value for the editBox
3. Implement the onChange callback to perform validation, update the private variable, and use the IRibbonUI object to invalidate the editBox control. This triggers the ribbon to call the getText callback again, effectively updating the field.

An example:
This snippet when merged into the Ribbon class, will ensure that the text entered in an edit box ends with “of doom”

First, the ribbonxml part

<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
  <ribbon>
    <tabs>
      <tab id="DoomedObjects" label="DoomedObjects">
        <group id="spg" label="Stuff">
          <editBox id="txtDoomed" label="Doomed" onChange="OnChange" getText="GetText"/>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

And the c# for the Ribbon class

        private string textValue;
        private Microsoft.Office.Core.IRibbonUI ribbon;


        public void Ribbon_Load(Office.IRibbonUI ribbonUI)
        {
            this.ribbon = ribbonUI;
            textValue= "";
        }

        public void OnChange(Office.IRibbonControl control, string text)
        {
            if (!text.EndsWith("of doom"))
            {
                textValue = text + " of doom";
                ribbon.InvalidateControl(control.Id);
                //Only invalidating when the value needs to change. You could invalidate after the if block as well 
            }
            else
            {
                textValue = text;
            }
        }

        public string GetText(Office.IRibbonControl control)
        {
            return textValue;
        }


Behaviour of Application.MailMergeAfterRecordMerge Event

The signature for the Application.MailMergeAfterRecordMerge event looks like this:

        void Application_MailMergeAfterRecordMerge(Word.Document Doc)
        {
        }

While I would have expected the Document referenced in the Doc parameter to be the newly created merged document, it is actually a reference to the document from which the merge occurred. In other words, it’s not really all that useful.

Automate Word Mailmerge Preview Results

(Long story short: the property you need is ViewMailMergeFieldCodes. Set it to 0 to enable preview; -1 to disable preview)

The mail merge process in Microsoft Word is a bit of a bear to automate sometimes. I suppose I should be thankful that it can be automated at all, but there are bits of it that are very poorly documented, requring a bit of trial and error to work with. It doesn’t help that most Office interop documentation leans toward Visual Basic…

Anyway, today’s issue was attempting to programmatically Preview the results of a Mail Merge in a C# VSTO project, like the button in the ribbon:

Big surprise: Document.MailMerge doesn’t contain any properties or methods with the word “preview”

Turns out there is a property called ViewMailMergeFieldCodes that does what we need, but in reverse. Microsoft’s documentation states:

This property returns True if merge field names are displayed in a mail merge main document, and False if information from the current data record is displayed

Trouble is, the field is an int, not a bool! A little debugging to the rescue:
When this field is set to -1, Preview is disabled, and the field codes are displayed.
When set to 0, Preview is enabled, and the record data is displayed.

To switch the displayed record, change Document.MailMerge.DataSource.ActiveRecord.

Now if only there was an event that fired when the record changed…