My C# VSTO Add-In for Word helps generate specialized labels for a Mail Merge. Mailing Labels are created in the document, and the first label in the "table" of labels is populated programmatically:
Word.Field myField = myCell.Range.Fields.Add(range, missing, missing, missing);
myField.Code.Text = " MERGEFIELD MyField ";
Now, when Word creates the mailing labels for a mail merge, it puts field codes where the next record goes: they look like { NEXT }
or «Next Record»
in Word.
In Word's "Mailings" tab, there's a built-in button called Update Labels. Its Office Control ID is MailMergeUpdateLabels
. If you have a table of Mailing Labels open, it will duplicate everything in the first label into every single valid label space. It also ensures that the «Next Record»
fields begin every label after the first, so that the mail merge can insert the next record.
Before Update Labels / After Update Labels
I need to trigger this action programmatically through C#. It would be very cheeky if I were to tell the user "please go to the mailings tab and press the Update Labels button". I can think of three ways that this could be solved:
Trigger the same method that the Update Labels button uses through Word Interop.
- I've done a deep dive through the MSDN reference for Word Interop in C#. I have found no such method (and no,
myObject.Range.Fields.Update()
does not accomplish this.).
- I've done a deep dive through the MSDN reference for Word Interop in C#. I have found no such method (and no,
Reference the button and simulate a click.
- Something like
updateButton.PerformClick();
, but I have no idea how to expose the built-in button through Interop so that I could reference it in my code.
- Something like
Write a method that achieves the same result. After all, I might be chasing the XY Problem.
- I already wrote one. Unfortunately, it's very slow (~7 seconds for a page of 30 labels). The most time-consuming operation seems to be adding the fields to the ranges. The "Update Labels" button's effect is very flexible and near-instantaneous.
For reference, here is the method that I use to populate a single label cell:
private void InsertIntoCell(Word.Cell cell, bool insertNextField)
{
string[] fieldCodes = { @" NEXT ",
@" MERGEFIELD Name ",
@" MERGEFIELD SerialNumber \b "": "" ",
"\v",
@" MERGEBARCODE Barcode CODE128 " };
cell.Range.Delete();
Word.Range range = cell.Range;
range.Collapse(WdCollapseDirection.wdCollapseStart);
foreach(string fieldCode in fieldCodes)
{
//the NEXT field is the first one in the fieldcodes array
//skip it if the program asks not to insert it
if (!insertNextField)
{
insertNextField = true;
continue;
}
//if the field codes demand a Vertical Tab character
//write it, because word reads it as a newline character
if(fieldCode.Equals("\v"))
{
range.Text = fieldCode;
range.Move(WdUnits.wdCharacter, 1);
continue;
}
Word.Field field = cell.Range.Fields.Add(range, missing, missing, missing);
field.Code.Text = fieldCode;
//move the range to the end of the most recent field
range = field.Result;
range.Collapse(WdCollapseDirection.wdCollapseEnd);
}
//set font, update the fields to make them visible
cell.Range.Font.Size = FIELD_FONT_SIZE;
cell.Range.Fields.Update();
}
Again, this way is very slow. I could use Selections and programmatically copy-paste (yes, using the user's clipboard), but that seems dirty. Even then, Update Labels is still superior, because it "knows" where to put «Next Record»
if it is deleted by the user.
I am mostly curious about how Option 2 would be done. If I am shown how to expose the Update Labels button to clicks, I could learn to expose any button to simulated clicks.