C# Word Interop:在邮件中使用内置 "Update Labels" 函数

C# Word Interop: using built-in "Update Labels" function in Mailings

My C# VSTO Word 加载项有助于为邮件合并生成专门的标签。在文档中创建邮件标签,并以编程方式填充 "table" 标签中的第一个标签:

Word.Field myField = myCell.Range.Fields.Add(range, missing, missing, missing);
myField.Code.Text = " MERGEFIELD MyField ";

现在,当 Word 为邮件合并创建邮件标签时,它会将域代码放在下一条记录所在的位置:它们在 Word 中看起来像 { NEXT }«Next Record»

在 Word 的 "Mailings" 选项卡中,有一个名为 Update Labels 的内置按钮。它的 Office 控制 ID 是 MailMergeUpdateLabels。如果您打开了 table 个邮件标签,它将 将第一个标签中的所有内容复制到每个有效标签 space 中。它还确保 «Next Record» 字段在第一个标签之后开始每个标签,以便邮件合并可以插入下一条记录。

Before Update Labels / After Update Labels

我需要通过 C# 以编程方式触发此操作。如果我告诉用户"please go to the mailings tab and press the Update Labels button",那将是非常厚颜无耻的。我可以想到三种解决方法:

  1. 触发更新标签按钮通过 Word Interop 使用的相同方法。

    • 我已经深入研究了 C# 中 Word Interop 的 MSDN 参考。我没有找到这样的方法(不,myObject.Range.Fields.Update() 没有完成这个。)。
  2. 引用按钮并模拟点击。

    • 类似于 updateButton.PerformClick();,但我不知道如何通过 Interop 公开内置按钮,以便我可以在我的代码中引用它。
  3. 写一个实现同样结果的方法。毕竟我可能追的是XY Problem.

    • 我已经写了一个。不幸的是,它非常慢(一页 30 个标签大约需要 7 秒)。最耗时的操作似乎是将字段添加到范围。 "Update Labels" 按钮的效果非常灵活,几乎是即时的。

作为参考,这是我用来填充单个标签单元格的方法:

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();
    }

同样,这种方式很慢。我 可以 使用选择并以编程方式复制粘贴(是的,使用用户的剪贴板),但这看起来很脏。即便如此,Update Labels 仍然更胜一筹,因为它 "knows" 如果它被用户删除了 «Next Record» 放在哪里。

我主要好奇 选项 2 是如何完成的。如果向我展示如何将“更新标签”按钮暴露给点击,我可以学习将任何按钮暴露给模拟点击。

是的,可以同时执行选项 1 和选项 2。这两种方法都有点晦涩难懂,所以您没有遇到它们也就不足为奇了。

选项 1:由于某些奇怪的原因,该命令未包含在标准对象模型中。但它可以通过 late bound WordBasic 对象模型访问。 (实际上,Word 中的所有命令都有相应的 WordBasic 实现,因为在幕后仍然运行所有内容。但它们没有记录...)

VB 语言可以直接使用它。但是,C# 无法直接使用后期绑定对象,因此您需要 PInvoke:

object wordBasic = wdApp.GetType().InvokeMember("WordBasic", BindingFlags.GetProperty, null, wdApp, null);
wordBasic.GetType().InvokeMember("MailMergePropagateLabel", BindingFlags.InvokeMethod, null, wordBasic, null);

选项 2:所有 Office 应用程序都可以访问共享对象模型的 CommandBars 部分。最初,它负责菜单和工具栏用户界面(Office 2007 之前的版本)。在 Office 2007 中,它被功能区 UI 取代,该对象被保留以实现向后兼容性 并且 扩展为与功能区的有限交互 UI.

其中一个 "new" 方法是 ExecuteMso,它执行按钮命令,就好像它已被单击一样:

wdApp.CommandBars.ExecuteMso("MailMergeUpdateLabels");