How to create nested fields using C# Interop.Word (IF option with MailMerge Fields) -
i trying set mailmerge fields , let word fill them me, isn't problem @ all... looking , can't figure out following, want set 2 mailmergefields in 1 place , let word sort out me.
in case have mergefield po_box , adress, if there po_box # use it, otherwise use standard adress.
example of mailmerge in word:
{ if { mergefield po_box } > "1" "{ mergefield po_box }" "{ mergefield adress }" \* mergeformat }
is there way make happen thru 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 document word.document wrddoc = wrdapp.documents.add(ref filename, visible: true); wrdselection = wrdapp.selection; wrdmailmerge = wrddoc.mailmerge; // open data source .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 want combine field po_box , let word // 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 line , insert date field current date. wrdselection.paragraphformat.alignment = word.wdparagraphalignment.wdalignparagraphright; object objdate = "dd.mm.yyyy"; wrdselection.insertdatetime(ref objdate); //preview final merge wrddoc.mailmerge.destination = word.wdmailmergedestination.wdsendtonewdocument; wrddoc.mailmerge.execute(); //close template object saveoption = word.wdsaveoptions.wddonotsavechanges; wrddoc.close(ref saveoption); //shows application after process user wrdapp.visible = true; } public static void insertlines(word.application wrdapp, int linenum) { int icount; // insert "linenum" blank lines. (icount = 1; icount <= linenum; icount++) { wrdapp.selection.typeparagraph(); } }
so basicly have, need adress mergefield behave described above, since receive .csv data programm can't modify place field in word sort out if there po box or adress.
so, want create nested field codes. there 2 basic approaches this:
- record macro while doing user basis. relies on selection object, can tricky; approach not scalable (only works specific combination). described on stackoverflow, won't repeat here: setting nested field in word using vba
- insert field string, using "placeholders" indicate field codes are, convert placeholders field codes. scalable: can used combination of fields. there excellent algorithm in c# posted on github, florian wolters in response discussion in participated on msdn. copy below convenience.
https://gist.github.com/florianwolters/6257233
//------------------------------------------------------------------------------ // <copyright file="fieldcreator.cs" company="florian wolters"> // copyright (c) florian wolters. 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> /// class <see cref="fieldcreator"/> simplifies creation of <see cref="word.field"/>s. /// </summary> public class fieldcreator { /// <summary> /// adds 1 or more new <see cref="word.field"/> specified <see cref="word.range"/>. /// <para> /// method allows insert nested fields @ specified range. /// </para> /// <example> /// <c>insertfield(application.selection.range, {{= {{page}} - 1}};</c> /// produce /// { = { page } - 1 } /// </example> /// </summary> /// <param name="range">the <see cref="word.range"/> add <see cref="word.field"/>.</param> /// <param name="thestring">the string convert 1 or more <see cref="word.field"/> objects.</param> /// <param name="fieldopen">the special code mark start of <see cref="word.field"/>.</param> /// <param name="fieldclose">the special code mark end of <see cref="word.field"/>.</param> /// <returns>the newly created <see cref="word.field"/></returns> /// <remarks> /// solution vba has been taken <a href="http://stoptyping.co.uk/word/nested-fields-in-vba">this</a> /// article , adopted c# 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 not check this, algorithm breaks. if (thestring == fieldopen + fieldclose) { return this.insertempty(range); } // todo implement additional error handling. // todo possible remove dependency 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 marker comes first. if (nextopen.start < nextclose.start) { nextopen.text = string.empty; searchrange.start = nextopen.end; // field open, push new range stack. fieldstack.push(nextopen.duplicate); } else { nextclose.text = string.empty; // move start of main search region onwards past end marker. searchrange.start = nextclose.end; // field close, pop last range stack , insert field. fieldrange = (word.range)fieldstack.pop(); fieldrange.end = nextclose.end; result = this.insertempty(fieldrange); } } // move current selection after inserted fields. // todo improvement possible, e.g. using range object? int newpos = fieldrange.end + fieldrange.fields.count + 1; fieldrange.setrange(newpos, newpos); fieldrange.select(); // update result of outer field object. result.update(); return result; } } /// <summary> /// adds new empty <see cref="word.field"/> specified <see cref="word.range"/>. /// </summary> /// <param name="range">the <see cref="word.range"/> add <see cref="word.field"/>.</param> /// <param name="preserveformatting"> /// whether apply formatting of previous <see cref="word.field"/> result 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 field codes of empty field, because otherwise can't sure visible. result.showcodes = true; return result; } /// <summary> /// creates <see cref="word.field"/> , adds specified <see cref="word.range"/> /// </summary> /// <remarks> /// <see cref="word.field"/> added <see cref="word.fields"/> collection of specified <see /// cref="word.range"/>. /// </remarks> /// <param name="range">the <see cref="word.range"/> add <see cref="word.field"/>.</param> /// <param name="type">the type of <see cref="word.field"/> create.</param> /// <param name="preserveformatting"> /// whether apply formatting of previous field result new result. /// </param> /// <param name="text">additional text needed <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 next closing field 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; } } }
Comments
Post a Comment