Home |
Search |
Today's Posts |
#1
![]()
Posted to microsoft.public.word.mailmerge.fields
|
|||
|
|||
![]()
Office professional 2003
Visual studio 2005 C# Example mail merge field from header: «Field:Name» I have smart client that use web services to provide data for mail merge. The templates contain Custom tokens inserted in the document as mail merge fields eg {{Field:StudentNumber}}, {{Table:CourseUnits}}. The client parses the tokens and populates document with the relevant data. All works well until these fields are in the header or footer. The main method for performing the merge loops through the supplied data rows and calls the ProcessFields method passing in the fields collection from the document, header and footer. Code stub from the execute method DataRowCollection rows = _Data.Tables[0].Rows; for (int i=0; i rows.Count; i++) { Word.Document document = null; if (_DocumentType == DocumentType.Template) { document = _Assistant.AddDocument(_FilePath, true); } else { document = _Assistant.OpenDocument(_FilePath, true); } if (_BeforeRowProcessed != null) { _BeforeRowProcessed(this, document, rows[i], i); } // document.MailMerge.Destination = _Destination; Word.HeaderFooter header = document.Sections[1].Headers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary]; if (header.Exists) { ProcessFields(header.Range.Fields, rows[i]); } ProcessFields(document.Fields, rows[i]); Word.HeaderFooter footer = document.Sections[1].Footers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary]; if (footer.Exists) { ProcessFields(footer.Range.Fields, rows[i]); } if (_AfterRowProcessed != null) { _AfterRowProcessed(this, document, rows[i], i); } } private void ProcessFields(Word.Fields fields, DataRow row) { foreach (Word.Field field in fields) { field.Select(); Word.Range range = _Assistant.Application.Selection.Range; if (_OnFieldSelect != null) { _OnFieldSelect(this, field, row); } ResolveToken(field, row); } } The resolve ResolveToken method maps the word merge field to the database field writing the text with: field.Result.Text = DatabaseAssistant.SafeStringValue(row[tokenKey]); The best I have mamanged is to have either of the header or footer edit pane open with the resolved value however when I close the window the merge field only is displayed - the value is gone. I cannot see the value in the print preview either. Initially I'm only concerned with the primary header or footer - old tackle the variations of headers / footers at a later stage. This is the first Word automation I've built so any pointers will be appreciated. JS |
#2
![]()
Posted to microsoft.public.word.mailmerge.fields
|
|||
|
|||
![]()
Hi Jimmi,
Hmmm. "Mail merge" is a very specific thing in Word terminology. Given that you're working with a web service, you can't be doing a true mail merge, so you shouldn't use that term when discussing your project - it confuses people like us :-) OK, so you're passing data into "targets" in a Word document, and you've chosen to use Mergefields (I guess, it's hard to be sure if that's what you really have) as the targets. This works in the body of the document, but not in the header/footer. The reason is fairly clear: you're trying to use the Selection object. Fact is, physically selecting a header/footer via automation in a Word document just doesn't work very well. Actually, you should avoid *selecting* anything when automating Word, unless there's no other way to accomplish the task. It's much better to work exclusively (if possible) with the RANGE object. You do this up the point of passing control to the ProcessFields method. A field obejct doesn't have a Range property, but it does have a Result property which returns a Range. You do not have to select the field in order to access the Result property. Next problem: Word fields are actually dynamic entities - they aren't meant to serve as "data targets". With one single exception (form fields), if you assign something to Field.Result.Text that information will be lost as soon as the field receives a command to update (such as pressing F9 when the field is selected). This is how Word is designed to work. If the data you're passing in should be permanent, then you should work more like this: Word.Field fld = footer.Fields[i]; Word.Range rng = fld.Result; fld.Delete; rng.Text = "my data"; [i] Office professional 2003 Visual studio 2005 C# Example mail merge field from header: «Field:Name» I have smart client that use web services to provide data for mail merge. The templates contain Custom tokens inserted in the document as mail merge fields eg {{Field:StudentNumber}}, {{Table:CourseUnits}}. The client parses the tokens and populates document with the relevant data. All works well until these fields are in the header or footer. The main method for performing the merge loops through the supplied data rows and calls the ProcessFields method passing in the fields collection from the document, header and footer. Code stub from the execute method DataRowCollection rows = Data.Tables[0].Rows; for (int i=0; i rows.Count; i++) { Word.Document document = null; if ( DocumentType == DocumentType.Template) { document = Assistant.AddDocument( FilePath, true); } else { document = Assistant.OpenDocument( FilePath, true); } if ( BeforeRowProcessed != null) { BeforeRowProcessed(this, document, rows[i], i); } // document.MailMerge.Destination = Destination; Word.HeaderFooter header = document.Sections[1].Headers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary]; if (header.Exists) { ProcessFields(header.Range.Fields, rows[i]); } ProcessFields(document.Fields, rows[i]); Word.HeaderFooter footer = document.Sections[1].Footers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary]; if (footer.Exists) { ProcessFields(footer.Range.Fields, rows[i]); } if ( AfterRowProcessed != null) { AfterRowProcessed(this, document, rows, i); } } private void ProcessFields(Word.Fields fields, DataRow row) { foreach (Word.Field field in fields) { field.Select(); Word.Range range = Assistant.Application.Selection.Range; if ( OnFieldSelect != null) { OnFieldSelect(this, field, row); } ResolveToken(field, row); } } The resolve ResolveToken method maps the word merge field to the database field writing the text with: field.Result.Text = DatabaseAssistant.SafeStringValue(row[tokenKey]); The best I have mamanged is to have either of the header or footer edit pane open with the resolved value however when I close the window the merge field only is displayed - the value is gone. I cannot see the value in the print preview either. Initially I'm only concerned with the primary header or footer - old tackle the variations of headers / footers at a later stage. This is the first Word automation I've built so any pointers will be appreciated. Cindy Meister INTER-Solutions, Switzerland http://homepage.swissonline.ch/cindymeister (last update Jun 17 2005) http://www.word.mvps.org This reply is posted in the Newsgroup; please post any follow question or reply in the newsgroup and not by e-mail :-) |
#3
![]()
Posted to microsoft.public.word.mailmerge.fields
|
|||
|
|||
![]()
Hi Cindy
Thank you so much for your advice. I could not have built a solution without your website and contributions to the newsgroups from yourself and the other contributing MVP's. Re "word terminology" and "Mail Merge" - I agree, and have adopted the "targets" term in my documentation. You are correct in your deductions - and I am using the fields as data targets and the data I'm passing in is to be permanent. I have implemented your suggested code block and it works beautifully. As I'm not performing a mail merge (there is no document.MailMerge.DataSource) I'm assuming I will need to implement my own custom code to emulate the WdMailMergeDestination.wdSendToNewDocument..wdSend ToPrinter functionality? I'm pretty sure I already know the answer. Many thanks again Jaimie Sims (e-Jimmi). |
#4
![]()
Posted to microsoft.public.word.mailmerge.fields
|
|||
|
|||
![]()
Hi Jaimie,
As I'm not performing a mail merge (there is no document.MailMerge.DataSource) I'm assuming I will need to implement my own custom code to emulate the WdMailMergeDestination.wdSendToNewDocument..wdSend ToPrinter functionality? I'm pretty sure I already know the answer. LOLYes, in that case you do have to code it all. Question then becomes, of course, what's the most efficient approach. One possibility would be to close the document and start over again with a new copy. But that would be comparatively slow. I might be more inclined to begin by setting a BOOKMARK around each mergefield target (do that in your loop, rather than writing in the text). Then loop. In the loop, write the text to Bookmark.Range.Text (which should remove the mergefield on the first pass), and then recreate the bookmark so that it will always be there. Very roughly, in VBA-speak: Dim rng as Word.Range Dim bkmName as String Set rng = document.Bookmarks(bkmName).Range rng.Text = "the data" document.Bookmarks.Add Name:=bkmName, Range:=rng Cindy Meister INTER-Solutions, Switzerland http://homepage.swissonline.ch/cindymeister (last update Jun 17 2005) http://www.word.mvps.org This reply is posted in the Newsgroup; please post any follow question or reply in the newsgroup and not by e-mail :-) |
#5
![]()
Posted to microsoft.public.word.mailmerge.fields
|
|||
|
|||
![]()
Hi Cindy
Thanks again for going the extra yards. I'll look at implmenting this in the coming weeks (other bigger fires to deal with now). take care JS Then loop. In the loop, write the text to Bookmark.Range.Text (which should remove the mergefield on the first pass), and then recreate the bookmark so that it will always be there. Very roughly, in VBA-speak: Dim rng as Word.Range Dim bkmName as String Set rng = document.Bookmarks(bkmName).Range rng.Text = "the data" document.Bookmarks.Add Name:=bkmName, Range:=rng |
Reply |
Thread Tools | |
Display Modes | |
|
|
![]() |
||||
Thread | Forum | |||
Header and Footer | Microsoft Word Help | |||
Word & WordPerfect | Microsoft Word Help | |||
Pre-set Query Conditions that cannot be changed | Mailmerge | |||
Using Word Mail Merge feature for custom templates | Mailmerge | |||
Specific Email Merge w/ Specific Attachements | Mailmerge |