[英]How to create nested fields using C# Interop.Word (IF option with MailMerge Fields)
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. 我正在尝试设置MailMerge字段并让Word为我填充它们,这根本不是问题...我想要做的并且无法弄清楚的是以下内容,我想在1中设置2个MailMergeFields放置并让Word为我整理。
In this case have a mergefield for PO_Box and Adress, if there is a PO_Box # use it, otherwise use the Standard Adress. 在这种情况下,有一个PO_Box和Adress的mergefield,如果有一个PO_Box,请使用#,否则使用标准地址。
Example of the MailMerge what it would look like in Word: MailMerge的示例在Word中的外观:
{ IF { MERGEFIELD PO_Box } > "1" "{ MERGEFIELD PO_Box }" "{ MERGEFIELD Adress }" \* MERGEFORMAT }
Is there a way to make this happen thru some Word Interop Funktion? 有没有办法通过一些字互操作来做到这一点?
Edit: 编辑:
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();
}
}
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. 所以这基本上是我所拥有的,现在我需要Adress MergeField像我上面描述的那样工作,因为我将从另一个无法修改的程序接收到一个.csv数据,所以我想将此字段放在Word中以进行排序确定是否有邮政信箱或地址。
So, what you really want is to create the nested field codes. 因此,您真正想要的是创建嵌套的域代码。 There are two basic approaches for this:
有两种基本方法:
https://gist.github.com/FlorianWolters/6257233 https://gist.github.com/FlorianWolters/6257233
//------------------------------------------------------------------------------
// <copyright file="FieldCreator.cs" company="Florian Wolters">
// Copyright (c) Florian Wolters. All rights reserved.
// </copyright>
// <author>Florian Wolters <wolters.fl@gmail.com></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;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.