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.
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
- How to Use DOCVARIABLE Fields in a Document
- How to: Embed Images into a Mail Merge Template
- How to: Send a Mail-Merge Document as an E-mail
- How to: Import HTML Files that Contain Images Referenced with a Custom Prefix
Documentation
- Mail Merge in Word Processing Document API
- How to: Insert Dynamic Content
- How to: Replace a Placeholder with a Document Element
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
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;
}
}
}
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;
}
}
}