This demo illustrates how to use the mail merge functionality to generate personalized letters with the help of the Word Processing Document API. Select a sender (Employee), an addressee (Customer), and a document format to export the result. Click Download to save the document to your computer.
@model AspNetCoreDemos.OfficeFileAPI.WordRTFMailMergeModel
@using DevExtreme.AspNet.Mvc
@using (Html.BeginForm("WordRTFMailMergeExportTo", "MailMerge", FormMethod.Post)) {
<script type="text/javascript">
function UpdatePreview() {
var EmployeeIdValue = $("#EmployeesBox").dxSelectBox('instance').option('value');
var CustomerIdValue = $("#CustomersBox").dxSelectBox('instance').option('value');
var args = "EmployeeID=" + encodeURIComponent(EmployeeIdValue) +
"&CustomerID=" + encodeURIComponent(CustomerIdValue);
WordRTFPreview.Update(args);
}
</script>
<div class="demo-view-container">
@await Html.PartialAsync("WordRTFPreviewPartial", Model.PreviewModel)
</div>
<div class="options">
<div class="caption">Options</div>
<div class="option">
<div class="label">Employee Id</div>
@(Html.DevExtreme().SelectBoxFor(m => m.EmployeeId)
.ID("EmployeesBox")
.DataSource(Model.Employees
.Select(i => new { Value = i.EmployeeID, Text = i.FirstName + " " + i.LastName }))
.ValueExpr("Value")
.DisplayExpr("Text")
.OnValueChanged("UpdatePreview")
)
</div>
<div class="option">
<div class="label">Customer Id</div>
@(Html.DevExtreme().SelectBoxFor(m => m.CustomerId)
.ID("CustomersBox")
.DataSource(Model.Customers
.Select(i => new { Value = i.CustomerID, Text = i.ContactName }))
.ValueExpr("Value")
.DisplayExpr("Text")
.OnValueChanged("UpdatePreview")
)
</div>
@await Html.PartialAsync("WordRTFDocumentDownloaderPartial", Model)
</div>
}
@model AspNetCoreDemos.OfficeFileAPI.WordRTFPreviewModel
<iframe id="previewFrame" src="@Url.Action(Model.PreviewDocumentAction, Model.ControllerName)" height="@Model.IFrameSize" class="demo-preview-border" style="width:100%;box-sizing:border-box"></iframe>
<script type="text/javascript">
WordRTFPreview = {
basePath: '@Url.Action(Model.PreviewDocumentAction, Model.ControllerName)',
Update: function (param) {
var iframeElementName = "previewFrame";
var iframeElement = document.getElementById(iframeElementName);
if (!iframeElement)
return;
var additionalParams = "&" + new Date().valueOf();
if (param)
additionalParams = param;
iframeElement.src = this.basePath + "?" + additionalParams;
}
};
</script>
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace AspNetCoreDemos.OfficeFileAPI {
public partial class MailMergeController : OfficeDemoController {
public IActionResult WordRTFMailMerge() {
WordRTFMailMergeModel model = new WordRTFMailMergeModel();
return View(model);
}
}
}
using System.Data;
using System.IO;
using DevExpress.Office.Services;
using DevExpress.Web.Office;
using DevExpress.XtraRichEdit;
using Microsoft.AspNetCore.Mvc;
namespace AspNetCoreDemos.OfficeFileAPI {
public partial class MailMergeController : OfficeDemoController {
const string mailMergeTemplateFilePath = "/Documents/MailMerge.rtf";
public IActionResult WordRTFPreviewMailMerge(WordRTFMailMergeModel model) {
Stream stream = ExecuteMerge(DocumentFormat.Html, model.Employee, model.Customer);
return CreatePreviewResult(stream);
}
public IActionResult WordRTFMailMergeExportTo(WordRTFMailMergeModel model) {
RichEditFileFormat documentType = model.FileFormat;
DocumentFormat documentFormat = WordRTFUtils.ConvertToFormat(documentType);
Stream stream = ExecuteMerge(documentFormat, model.Employee, model.Customer);
if (stream == null)
return new EmptyResult();
string contentType = WordRTFUtils.ConvertToContentType(documentType);
string fileExtension = WordRTFUtils.ConvertToFileExtension(documentType);
return CreateFileStreamResult(stream, contentType, fileExtension);
}
Stream ExecuteMerge(DocumentFormat documentFormat, Employee employee, Customer customer) {
DataTable mailMergeDataTable = new DataTable();
AddDataColumns(mailMergeDataTable);
RichEditDocumentServer documentServer = new RichEditDocumentServer();
IUriStreamService uriService = (IUriStreamService)documentServer.GetService(typeof(IUriStreamService));
uriService.RegisterProvider(new WordRTFMailMergeDBUriStreamProvider(mailMergeDataTable, "Photo", this));
Stream result = null;
if (!PutMergeData(mailMergeDataTable, employee, customer))
return null;
string filePath = HostingEnvironment.ContentRootPath + mailMergeTemplateFilePath;
documentServer.LoadDocument(filePath);
documentServer.Options.MailMerge.DataSource = mailMergeDataTable;
documentServer.Options.MailMerge.ViewMergedData = true;
documentServer.Options.Export.Html.EmbedImages = true;
result = new MemoryStream();
if (documentFormat == DocumentFormat.Undefined) {
documentServer.Options.MailMerge.ActiveRecord = 0;
documentServer.ExportToPdf(result);
}
else {
DevExpress.XtraRichEdit.API.Native.MailMergeOptions options = documentServer.CreateMailMergeOptions();
options.DataSource = mailMergeDataTable;
options.FirstRecordIndex = 0;
options.LastRecordIndex = 0;
documentServer.MailMerge(options, result, documentFormat);
}
result.Seek(0, SeekOrigin.Begin);
return result;
}
void AddDataColumns(DataTable mailMergeDataTable) {
mailMergeDataTable.Columns.Add("EmployeeID", typeof(int));
mailMergeDataTable.Columns.Add("FirstName", typeof(string));
mailMergeDataTable.Columns.Add("LastName", typeof(string));
mailMergeDataTable.Columns.Add("ContactName", typeof(string));
mailMergeDataTable.Columns.Add("Employees.Address", typeof(string));
mailMergeDataTable.Columns.Add("Employees.City", typeof(string));
mailMergeDataTable.Columns.Add("Employees.PostalCode", typeof(string));
mailMergeDataTable.Columns.Add("ContactTitle", typeof(string));
mailMergeDataTable.Columns.Add("CompanyName", typeof(string));
mailMergeDataTable.Columns.Add("Customers.Address", typeof(string));
mailMergeDataTable.Columns.Add("Customers.City", typeof(string));
mailMergeDataTable.Columns.Add("Customers.PostalCode", typeof(string));
mailMergeDataTable.Columns.Add("Title", typeof(string));
mailMergeDataTable.Columns.Add("Photo", typeof(object));
}
bool PutMergeData(DataTable mailMergeDataTable, Employee employee, Customer customer) {
mailMergeDataTable.Rows.Clear();
mailMergeDataTable.Rows.Add(employee.EmployeeID,
employee.FirstName,
employee.LastName,
customer.ContactName,
employee.Address,
employee.City,
employee.PostalCode,
customer.ContactTitle,
customer.CompanyName,
customer.Address,
customer.City,
customer.PostalCode,
employee.Title,
null);
return true;
}
}
public class WordRTFMailMergeDBUriStreamProvider : IUriStreamProvider {
static readonly string prefix = "dbimg://";
DataTable table;
string columnName;
MailMergeController pathMapper;
public WordRTFMailMergeDBUriStreamProvider(DataTable table, string columnName, MailMergeController controller) {
this.table = table;
this.columnName = columnName;
this.pathMapper = controller;
}
Stream IUriStreamProvider.GetStream(string uri) {
uri = uri.Trim();
if (!uri.StartsWith(prefix))
return null;
string strId = uri.Substring(prefix.Length).Trim();
int id;
if (!int.TryParse(strId, out id))
return null;
string pictureRoot = pathMapper.HostingEnvironment.ContentRootPath + "/Documents/";
string fileName = string.Format("{0}Photo{1}.jpeg", pictureRoot, id);
byte[] bytes = null;
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
int length = (int)fs.Length;
bytes = new byte[length];
fs.Read(bytes, 0, length);
}
return new MemoryStream(bytes);
}
}
}
using System.Collections.Generic;
namespace AspNetCoreDemos.OfficeFileAPI {
public class WordRTFMailMergeModel : WordRTFModelBase {
public WordRTFMailMergeModel() {
PreviewModel.PreviewDocumentAction = "WordRTFPreviewMailMerge";
PreviewModel.ControllerName = "MailMerge";
Data = new InMemoryNWindData();
Customers = Data.Customers;
Employees = Data.Employees;
EmployeeId = Employees[0].EmployeeID;
CustomerId = Customers[0].CustomerID;
}
public Employee Employee { get { return Employees.Find(item => item.EmployeeID == EmployeeId); } }
public Customer Customer { get { return Customers.Find(item => item.CustomerID == CustomerId); } }
public int EmployeeId { get; set; }
public string CustomerId { get; set; }
public List<Customer> Customers { get; private set; }
public List<Employee> Employees { get; private set; }
public InMemoryNWindData Data { get; set; }
}
}
namespace AspNetCoreDemos.OfficeFileAPI {
public class WordRTFModelBase {
public WordRTFModelBase() {
PreviewModel = new WordRTFPreviewModel();
PreviewModel.OwnerPropertyName = "PreviewModel";
FileFormat = RichEditFileFormat.Rtf;
}
public RichEditFileFormat FileFormat { get; set; }
public WordRTFPreviewModel PreviewModel { get; internal set; }
}
public class WordRTFPreviewModel {
public WordRTFPreviewModel() {
}
public string OwnerPropertyName { get; set; }
public string PreviewDocumentAction { get; set; }
public string ControllerName { get; set; }
public int IFrameSize { get; set; } = 452;
}
}