如何使用C#Interop.Word创建嵌套字段(带有MailMerge Fields的IF选项) [英] How to create nested fields using C# Interop.Word (IF option with MailMerge Fields)

查看:122
本文介绍了如何使用C#Interop.Word创建嵌套字段(带有MailMerge Fields的IF选项)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试设置MailMerge字段,并让Word为我填充它们,这根本不是问题.我想要做的并且无法弄清楚的是以下内容,我想设置2 MailMergeFields放在1个位置,然后让Word为我整理.

I am trying to set MailMerge fields and let Word fill them for me, which isn't a Problem at all... What i am looking to do and can't figure out is the following, I want to set 2 MailMergeFields in 1 place and let Word sort it out for me.

在这种情况下,有一个PO_Box和Adress的合并字段,如果有一个PO_Box,请使用#,否则使用标准地址.

In this case have a mergefield for PO_Box and Adress, if there is a PO_Box # use it, otherwise use the Standard Adress.

MailMerge的示例在Word中的外观:

Example of the MailMerge what it would look like in Word:

{ IF { MERGEFIELD PO_Box } > "1" "{ MERGEFIELD PO_Box }" "{ MERGEFIELD Adress }" \* MERGEFORMAT }

是否有办法通过Word Interop Funktion做到这一点?

Is there a way to make this happen thru some Word Interop Funktion?

        static void Main(string[] args)
    {
        object fileName = @"C:\test.docx";
        string dataSource = @"C:\Test.csv";


        Word.Selection wrdSelection;
        Word.MailMerge wrdMailMerge;
        Word.MailMergeFields wrdMergeFields;

        // Start Word Application
        Word.Application wrdApp = new Word.Application();

        //Load a document
        Word.Document wrdDoc = wrdApp.Documents.Add(ref fileName, Visible: true);

        wrdSelection = wrdApp.Selection;
        wrdMailMerge = wrdDoc.MailMerge;

        // Open Data Source from .csv file
        wrdDoc.MailMerge.OpenDataSource(dataSource);


        //Create MergeFields
        wrdSelection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft;
        wrdSelection.ParagraphFormat.LineSpacingRule = Word.WdLineSpacing.wdLineSpaceSingle;
        wrdSelection.ParagraphFormat.SpaceAfter = 0.0F;
        wrdMergeFields = wrdMailMerge.Fields;
        wrdMergeFields.Add(wrdSelection.Range, "Title");
        wrdSelection.TypeText(" ");
        wrdMergeFields.Add(wrdSelection.Range, "FirstName");
        wrdSelection.TypeText(" ");
        wrdMergeFields.Add(wrdSelection.Range, "LastName");
        wrdSelection.TypeParagraph();

        // Here I want to combine this Field with a PO_Box and let Word
        // do the trick
        wrdMergeFields.Add(wrdSelection.Range, "Address");
        wrdSelection.TypeParagraph();


        wrdMergeFields.Add(wrdSelection.Range, "City");
        wrdSelection.TypeText(", ");
        wrdMergeFields.Add(wrdSelection.Range, "State");
        wrdSelection.TypeText(" ");
        wrdMergeFields.Add(wrdSelection.Range, "Zip");
        wrdSelection.ParagraphFormat.LineSpacingRule = Word.WdLineSpacing.wdLineSpaceDouble;

        insertLines(wrdApp, 2);

        //Right justify the line and insert a date field with current date.
        wrdSelection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphRight;

        object objDate = "dd.MM.yyyy";
        wrdSelection.InsertDateTime(ref objDate);

        //Preview the final merge
        wrdDoc.MailMerge.Destination = Word.WdMailMergeDestination.wdSendToNewDocument;
        wrdDoc.MailMerge.Execute();

        //Close Template
        object saveOption = Word.WdSaveOptions.wdDoNotSaveChanges;
        wrdDoc.Close(ref saveOption);

        //Shows the Application after the process to the User
        wrdApp.Visible = true;
    }

    public static void insertLines(Word.Application wrdApp, int LineNum)
    {
        int iCount;

        // Insert "LineNum" blank lines.    
        for (iCount = 1; iCount <= LineNum; iCount++)
        {
            wrdApp.Selection.TypeParagraph();
        }
    }

所以这基本上就是我所拥有的,现在我需要Adress MergeField像我上面描述的那样工作,因为我将从另一个无法修改的程序接收到一个.csv数据,所以我想将此字段放在Word中它会理清是否有邮政信箱或地址.

So this basicly what I have, now i need the Adress MergeField to behave as i described above, since I will receive a .csv data from another programm that i can't modify I would like to place this field in Word that it will sort out if there is a PO Box or Adress.

推荐答案

因此,您真正想要的是创建嵌套的域代码.有两种基本方法:

So, what you really want is to create the nested field codes. There are two basic approaches for this:

  1. 在以用户身份进行操作的同时记录宏.这依赖于Selection对象,这可能很棘手.该方法不可扩展(仅适用于该特定组合).这是在StackOverflow上介绍的,因此在此不再赘述:使用VBA在Word中设置嵌套字段
  2. 将字段作为字符串插入,使用占位符"表示 域代码所在的位置,然后将占位符转换为字段 代码.这是可扩展的:它可以用于以下各项的任意组合 领域.弗洛里安·沃尔特斯(Florian Wolters)在GitHub上发布了C#中的一种出色算法,以回应我参与MSDN的讨论.为了方便起见,我将其复制到下面.
  1. Record a macro while doing it as a user as the basis. This relies on the Selection object, which can be tricky; the approach is not scalable (only works for that specific combination). This is described on StackOverflow, so I won't repeat it here: Setting up a nested field in Word using VBA
  2. Insert the field as a string, using "placeholders" to indicate where the field codes are, then convert the placeholders to field codes. This is scalable: it can be used for any combination of fields. There is an excellent algorithm in C# posted on GitHub, by Florian Wolters in response to a discussion in which I participated on MSDN. I copy it below for convenience.

https://gist.github.com/FlorianWolters/6257233

//------------------------------------------------------------------------------ 
// <copyright file="FieldCreator.cs" company="Florian Wolters"> 
//     Copyright (c) Florian Wolters. All rights reserved. 
// </copyright> 
// <author>Florian Wolters &lt;wolters.fl@gmail.com&gt;</author> 
//------------------------------------------------------------------------------ 
namespace FlorianWolters.Office.Word.Fields 
{ 
  using System; 
  using System.Collections; 
  using System.Runtime.InteropServices; 
  using Word = Microsoft.Office.Interop.Word; 

  /// <summary> 
  /// The class <see cref="FieldCreator"/> simplifies the creation of <see cref="Word.Field"/>s. 
  /// </summary> 
  public class FieldCreator 
  { 
     /// <summary> 
     /// Adds one or more new <see cref="Word.Field"/> to the specified <see cref="Word.Range"/>. 
     /// <para> 
     /// This method allows to insert nested fields at the specified range. 
     /// </para> 
     /// <example> 
     /// <c>InsertField(Application.Selection.Range, {{= {{PAGE}} - 1}};</c> 
     /// will produce 
     /// { = { PAGE } - 1 } 
     /// </example> 
     /// </summary> 
     /// <param name="range">The <see cref="Word.Range"/> where to add the <see cref="Word.Field"/>.</param> 
     /// <param name="theString">The string to convert to one or more <see cref="Word.Field"/> objects.</param> 
     /// <param name="fieldOpen">The special code to mark the start of a <see cref="Word.Field"/>.</param> 
     /// <param name="fieldClose">The special code to mark the end of a <see cref="Word.Field"/>.</param> 
     /// <returns>The newly created <see cref="Word.Field"/></returns> 
     /// <remarks> 
     /// A solution for VBA has been taken from <a href="http://stoptyping.co.uk/word/nested-fields-in-vba">this</a> 
     /// article and adopted for C# by the author. 
     /// </remarks> 
     public Word.Field InsertField( 
         Word.Range range, 
         string theString = "{{}}", 
         string fieldOpen = "{{", 
         string fieldClose = "}}") 
     { 
         if (null == range) 
         { 
             throw new ArgumentNullException("range"); 
         } 

         if (string.IsNullOrEmpty(fieldOpen)) 
         { 
             throw new ArgumentException("fieldOpen"); 
         }  

         if (string.IsNullOrEmpty(fieldClose)) 
         { 
             throw new ArgumentException("fieldClose"); 
         } 

         if (!theString.Contains(fieldOpen) || !theString.Contains(fieldClose)) 
         { 
             throw new ArgumentException("theString"); 
         } 

         // Special case. If we do not check this, the algorithm breaks. 
         if (theString == fieldOpen + fieldClose) 
         { 
             return this.InsertEmpty(range); 
         } 

         // TODO Implement additional error handling. 
         // TODO Possible to remove the dependency to state capture? 
         using (new StateCapture(range.Application.ActiveDocument)) 
         { 
             Word.Field result = null; 
             Stack fieldStack = new Stack(); 

             range.Text = theString; 
             fieldStack.Push(range); 

             Word.Range searchRange = range.Duplicate; 
             Word.Range nextOpen = null; 
             Word.Range nextClose = null; 
             Word.Range fieldRange = null; 

             while (searchRange.Start != searchRange.End) 
             { 
                 nextOpen = this.FindNextOpen(searchRange.Duplicate, fieldOpen); 
                 nextClose = this.FindNextClose(searchRange.Duplicate, fieldClose); 

                 if (null == nextClose) 
                 { 
                     break; 
                 } 

                 // See which marker comes first. 
                 if (nextOpen.Start < nextClose.Start) 
                 { 
                     nextOpen.Text = string.Empty; 
                     searchRange.Start = nextOpen.End; 

                     // Field open, so push a new range to the stack. 
                     fieldStack.Push(nextOpen.Duplicate); 
                 } 
                 else 
                 { 
                     nextClose.Text = string.Empty; 

                     // Move start of main search region onwards past the end marker. 
                     searchRange.Start = nextClose.End; 

                     // Field close, so pop the last range from the stack and insert the field. 
                     fieldRange = (Word.Range)fieldStack.Pop(); 
                     fieldRange.End = nextClose.End; 
                     result = this.InsertEmpty(fieldRange); 
                 } 
             } 

             // Move the current selection after all inserted fields. 
             // TODO Improvement possible, e.g. by using another range object? 
             int newPos = fieldRange.End + fieldRange.Fields.Count + 1; 
             fieldRange.SetRange(newPos, newPos); 
             fieldRange.Select(); 

             // Update the result of the outer field object. 
             result.Update(); 


             return result; 
         } 
     } 

     /// <summary> 
     /// Adds a new empty <see cref="Word.Field"/> to the specified <see cref="Word.Range"/>. 
     /// </summary> 
     /// <param name="range">The <see cref="Word.Range"/> where to add the <see cref="Word.Field"/>.</param> 
     /// <param name="preserveFormatting"> 
     /// Whether to apply the formatting of the previous <see cref="Word.Field"/> result to the new result. 
     /// </param> 
     /// <returns>The newly created <see cref="Word.Field"/>.</returns> 
     public Word.Field InsertEmpty(Word.Range range, bool preserveFormatting = false) 
     { 
         Word.Field result = this.AddFieldToRange(range, Word.WdFieldType.wdFieldEmpty, preserveFormatting); 

         // Show the field codes of an empty field, because otherwise we can't be sure that it is visible. 
         result.ShowCodes = true; 

         return result; 
     } 

     /// <summary> 
     /// Creates a <see cref="Word.Field"/> and adds it to the specified <see cref="Word.Range"/> 
     /// </summary> 
     /// <remarks> 
     /// The <see cref="Word.Field"/> is added to the <see cref="Word.Fields"/> collection of the specified <see 
     /// cref="Word.Range"/>. 
     /// </remarks> 
     /// <param name="range">The <see cref="Word.Range"/> where to add the <see cref="Word.Field"/>.</param> 
     /// <param name="type">The type of <see cref="Word.Field"/> to create.</param> 
     /// <param name="preserveFormatting"> 
     /// Whether to apply the formatting of the previous field result to the new result. 
     /// </param> 
     /// <param name="text">Additional text needed for the <see cref="Word.Field"/>.</param> 
     /// <returns>The newly created <see cref="Word.Field"/>.</returns> 
     private Word.Field AddFieldToRange( 
         Word.Range range, 
         Word.WdFieldType type, 
         bool preserveFormatting = false, 
         string text = null) 
     { 
         return range.Fields.Add( 
             range, 
             type, 
             (null == text) ? Type.Missing : text, 
             preserveFormatting); 
     } 

     private Word.Range FindNextOpen(Word.Range range, string text) 
     { 
         Word.Find find = this.CreateFind(range, text); 
         Word.Range result = range.Duplicate; 

         if (!find.Found) 
         { 
             // Make sure that the next closing field will be found first. 
             result.Collapse(Word.WdCollapseDirection.wdCollapseEnd); 
         } 

         return result; 
     } 

     private Word.Range FindNextClose(Word.Range range, string text) 
     { 
         return this.CreateFind(range, text).Found ? range.Duplicate : null; 
     } 

     private Word.Find CreateFind(Word.Range range, string text) 
     { 
         Word.Find result = range.Find; 
         result.Execute(FindText: text, Forward: true, Wrap: Word.WdFindWrap.wdFindStop); 

         return result; 
     } 
  } 
} 

这篇关于如何使用C#Interop.Word创建嵌套字段(带有MailMerge Fields的IF选项)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆