3
votes

I'm playing around with VSTO, more precisely with C# and a "Microsoft Word" application add-in, at the moment. I do want to programmatically create nested fields. I've come up with the following source code (for testing purposes):

public partial class ThisAddIn
{
    private void ThisAddIn_Startup(object sender, EventArgs e)
    {
        // TODO This is just a test.
        this.AddDocPropertyFieldWithinInsertTextField("Author", ".\\\\FileName.docx");
    }

    private void AddDocPropertyFieldWithinInsertTextField(string propertyName, string filePath)
    {
        // TODO Horrible, since we rely on the UI state.
        this.Application.ActiveWindow.View.ShowFieldCodes = true;

        Word.Selection currentSelection = this.Application.ActiveWindow.Selection;

        // Add a new DocProperty field at the current selection.
        currentSelection.Fields.Add(
            Range: currentSelection.Range,
            Type: Word.WdFieldType.wdFieldDocProperty,
            Text: propertyName,
            PreserveFormatting: false
        );

        // TODO The following fails if a DocProperty with the specified name does not exist.

        // Select the previously inserted field.
        // TODO This is horrible!
        currentSelection.MoveLeft(
            Unit: Word.WdUnits.wdWord,
            Count: 1,
            Extend: Word.WdMovementType.wdExtend
        );

        // Create a new (empty) field AROUND the DocProperty field.
        // After that, the DocProperty field is nested INSIDE the empty field.
        // TODO Horrible again!
        currentSelection.Fields.Add(
            currentSelection.Range,
            Word.WdFieldType.wdFieldEmpty,
            PreserveFormatting: false
        );

        // Insert text BEFORE the inner field.
        // TODO Horror continues.
        currentSelection.InsertAfter("INCLUDETEXT \"");

        // Move the selection AFTER the inner field.
        // TODO See above.
        currentSelection.MoveRight(
            Unit: Word.WdUnits.wdWord,
            Count: 1,
            Extend: Word.WdMovementType.wdExtend
        );

        // Insert text AFTER the nested field.
        // TODO See above.
        currentSelection.InsertAfter("\\\\" + filePath + "\"");

        // TODO See above.
        this.Application.ActiveWindow.View.ShowFieldCodes = false;

        // Update the fields.
        currentSelection.Fields.Update();
    }

Although the provided code covers the requirements, it has some major problems (as you can see if reading some of the code comments), e.g.:

  1. It isn't robust, since it relies on the UI state of the application.
  2. Is is verbose. The task isn't that complex, imo.
  3. It isn't easy to understand (Refactoring could help, but by solving problems 1. and 2., this drawback should disappear).

I did some research on the WWW, but haven't been able to find a clean solution, yet.

So, my question is:

  • Can someone provide (alternative) C# source code, that is more robust than mine and that allows me to add nested fields to a "Microsoft Word" document?
1
I can't do the code right now, but some hints: get the Range of the Selection and always work with Ranges; Setting the Range's TextRetrievalMode.IncludeFieldCodes to true may help avoid most if not all the UI State problems (it certainly does when you're trying to convert a set of fields to text; Always insert Empty field codes then add the field text (and anything else, if necessary) afterwards; Start with a text representaiton of the field (e.g. { IF "{ MERGEFIELD myfield }" = "abc" "{ REF something }" "{ REF other thing }" and process sequentially, inserting a field each time you hit a "}" - user1379931
(You'll also need an "escape" character if you want to represent ordinary "{" and "}" characters in the field. Alternatively, generate the OpenXML for the set of nested fields and use the Range object's InsertXML method to insert it. I haven't done it that way, but I can imagine that it could result in some reasonably elegant C# code. OTOH it may not be easy to verify that the fields are legal at the point you insert them, or that they will function correctly in that location. - user1379931

1 Answers

1
votes

I've created a generic implementation to create both non-nested and nested fields with VSTO for Microsoft Word. You can see the relevant source code in this Gist. I've ported the VBA source code from the article Nested Fields in VBA to C# and applied some improvements.

Still not perfect (needs some additional error handling), but far better than a solution with a Selection object, which relies on the state of the user interface!