Example T1281743
Visible to All Users

Word Processing Document API - How to Automate Mail Merge: Generate, Populate, and Export Documents

This sample project demonstrates how to use the mail merge feature to generate a template, populate it with data, and export the result to a DOCX file.

word processing document api mail merge result

Implementation Details

The RichEditDocumentServer.LoadDocument method call loads a document template. The AddMailMergeFields method implementation inserts the INCLUDEPICTURE and MERGEFIELD fields into the template's main body, and the INCLUDEPICTURE field to the footer.

The document template contains the DOCVARIABLE field that inserts the number of years the employee has worked at the company. The nested MERGEFIELD refers to the HireDate entry in the database. This field value is calculated in the CalculateDocumentVariable event handler.

The ImageStreamProvider class is used to insert images from a database. The GetStream method parses the received URI (the INCLUDEPICTURE field), finds the required data row, and returns the MemoryStream containing an image.

This project uses an XML file as the data source. The mail merge result is saved to the DOCX file.

Files to Review

C# Visual Basic
Program.cs Program.vb
ImageStreamProvider.cs ImageStreamProvider.vb

More Examples

Documentation

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

WordProcessingMailMerge/Program.cs(vb)
C#
using DevExpress.Office.Services; using DevExpress.XtraRichEdit; using DevExpress.XtraRichEdit.API.Native; using System.Data; using System.Diagnostics; namespace WordProcessingMailMerge { internal class Program { static void Main(string[] args) { DataSet xmlDataSet = new DataSet(); xmlDataSet.ReadXml("..\\..\\..\\Employees.xml"); xmlDataSet.Tables[0].PrimaryKey = new DataColumn[] { xmlDataSet.Tables[0].Columns[0] }; using (var wordProcessor = new RichEditDocumentServer()) { wordProcessor.LoadDocument("..\\..\\..\\template.docx"); AddMailMergeFields(wordProcessor.Document); wordProcessor.CalculateDocumentVariable += WordProcessor_CalculateDocumentVariable; // Register the URI provider service IUriStreamService uriStreamService = wordProcessor.GetService<IUriStreamService>(); uriStreamService.RegisterProvider(new ImageStreamProvider(xmlDataSet.Tables[0], "Photo")); MailMergeOptions myMergeOptions = wordProcessor.Document.CreateMailMergeOptions(); myMergeOptions.DataSource = xmlDataSet.Tables[0]; myMergeOptions.MergeMode = MergeMode.NewSection; wordProcessor.MailMerge(myMergeOptions, "result.docx", DocumentFormat.OpenXml); } Process.Start(new ProcessStartInfo("result.docx") { UseShellExecute = true }); } private static void AddMailMergeFields(Document document) { DocumentRange[] pictureRanges = document.FindAll("Photo", SearchOptions.WholeWord); foreach (var pictureRange in pictureRanges) { DocumentPosition picturePosition = pictureRange.End; // Delete the placeholder document.Delete(pictureRange); // Insert a field to a picture from a database Field pictureField = document.Fields.Create(picturePosition, @"INCLUDEPICTURE ""dbimg://{ placeholder }"""); // Find a placeholder for a nested MERGEFIELD DocumentRange nestedFieldRange = document.FindAll("{ placeholder }", SearchOptions.WholeWord, pictureField.CodeRange).First(); // Clear the placeholder range document.Delete(nestedFieldRange); // Create a nested field document.Fields.Create(nestedFieldRange.Start, "MERGEFIELD EmployeeID"); } // Find a placeholder for another image // in the footer SubDocument footer = document.Sections[0].BeginUpdateFooter(); DocumentRange[] imageRanges = footer.FindAll("image", SearchOptions.WholeWord); foreach (var imageRange in imageRanges) { DocumentPosition imagePosition = imageRange.End; // Delete the phrase footer.Delete(imageRange); // Create a field at the placeholder's position footer.Fields.Create(imagePosition, @"INCLUDEPICTURE ""DevExpress.png"""); } footer.EndUpdate(); // Find a placeholder for the FirstName field: DocumentRange[] nameRanges = document.FindAll("FirstName", SearchOptions.WholeWord); foreach (var nameRange in nameRanges) { DocumentPosition namePosition = nameRange.End; // Delete the phrase document.Delete(nameRange); // Create a field at the placeholder position document.Fields.Create(namePosition, @"MERGEFIELD ""FirstName"""); } } private static void WordProcessor_CalculateDocumentVariable(object sender, CalculateDocumentVariableEventArgs e) { if (e.Arguments.Count > 0) { // Retrieve the MERGEFIELD field value DateTimeOffset hireDate = Convert.ToDateTime(e.Arguments[0].Value); DateTimeOffset currentDate = DateTime.Now; // Calculate the difference between the current date // and the hire date var dif = currentDate.Subtract(hireDate); // Calculate the number of years int years = dif.Days / 365; // Specify the DOCVARIABLE field value e.Value = years.ToString(); e.Handled = true; } e.Handled = true; } } }
WordProcessingMailMerge/ImageStreamProvider.cs(vb)
C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WordProcessingMailMerge { using DevExpress.Office.Services; using System; using System.Data; using System.IO; public class ImageStreamProvider : IUriStreamProvider { static readonly string prefix = "dbimg://"; DataTable table; string columnName; public ImageStreamProvider(DataTable sourceTable, string imageColumn) { this.table = sourceTable; this.columnName = imageColumn; } public Stream GetStream(string uri) { // Parse the retrieved URI string uri = uri.Trim(); if (!uri.StartsWith(prefix)) return null; // Remove the prefix from the retrieved URI string string strId = uri.Substring(prefix.Length).Trim(); int id; // Check if the string contains the primary key if (!int.TryParse(strId, out id)) return null; // Retrieve the row that corresponds // with the key DataRow row = table.Rows.Find(id); if (row == null) return null; // Convert the image string from this row // to a byte array byte[] bytes = Convert.FromBase64String(row[columnName] as string) as byte[]; if (bytes == null) return null; // Return the MemoryStream with an image MemoryStream memoryStream = new MemoryStream(bytes); return memoryStream; } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.