Subtemplates
When the same terms and conditions, letterhead or address block belong in twenty templates, maintaining twenty copies is error-prone. A subtemplate keeps such content in one document that any template includes through an MS Word INCLUDETEXT field. During the merge, MailMerge replaces the field with the subtemplate, so an edit to the subtemplate reaches every template at once. In a WPF application, you insert these fields from the Reporting tab or from code, point MailMerge to the folder of the subtemplates and, where needed, provide the included content from your code.
The examples work with the invoice template invoice.docx and the data invoice.json of Merge Data into Fields. The design of templates in general is the subject of Template Design.
How Subtemplates Work
MailMerge treats an INCLUDETEXT field as a reference: it loads the document the field names and inserts the content at the field's position.
- The search path. The file name is looked up in the folder of MailMerge.SearchPath, which also holds the image files of image placeholders. With an empty search path, the root directory is used.
- Nested content is merged. A subtemplate may contain merge fields, merge blocks and
INCLUDETEXTfields of its own; they are merged with the same data as the template. - Formats. Subtemplates are loaded from files in formats such as DOCX, DOC and RTF.
- Headers and footers. Put an
INCLUDETEXTfield into the header of a template to share a page header, and design the content of the subtemplate in its main text rather than in its header.
Instead of a file, your code can provide the content of each field, for example from a database; see Supply Subtemplates From Code.
Insert an INCLUDETEXT Field With the Ribbon
The WPF Reporting tab offers INCLUDETEXT among its special fields:
- Place the input position where the subtemplate belongs.
- In the Merge Fields group, open the Insert Special Field drop-down and click IncludeText.
- Type the file name of the subtemplate, such as terms.docx, into the dialog box and click OK.
MailMerge always includes the whole document; a bookmark set in the field for MS Word is ignored.
Insert an INCLUDETEXT Field in Code
IncludeText creates the field for the WPF TextControl. Add a button with Click="insertTermsButton_Click" to MainWindow.xaml, and in MainWindow.xaml.cs using TXTextControl.DocumentServer.Fields; and this handler:
private void insertTermsButton_Click(object sender, RoutedEventArgs e)
{
IncludeText includeText = new IncludeText
{
// Searched in MailMerge.SearchPath during the merge
Filename = "terms.docx"
};
textControl1.ApplicationFields.Add(includeText.ApplicationField);
}
- The IncludeText constructor creates a new field, or, with an
ApplicationFieldas parameter, gives access to one that exists. - Filename names the subtemplate. TextFormat sets the case of the included text:
Uppercase,Lowercase,FirstCapital,TitleCaseorNone. Bookmark isn't used byMailMerge. ApplicationFields.Addinserts the field at the input position.
Saved as DOCX, the template keeps the field as an MS Word field; the LoadSettings of the merge, with ApplicationFieldFormat.MSWord, import it again.
Merge a Template With Subtemplates
Copy terms.docx into a folder Subtemplates of the application's output folder. The following handler sets the search path, merges the invoice in a ServerTextControl and loads the result into the editor; it needs using System;, using System.IO; and using TXTextControl.DocumentServer;:
private void mergeWithSubtemplatesButton_Click(object sender, RoutedEventArgs e)
{
byte[] result;
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
LoadSettings loadSettings = new LoadSettings
{
ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
LoadSubTextParts = true
};
tx.Load("invoice.docx", StreamType.WordprocessingML, loadSettings);
using (MailMerge mailMerge = new MailMerge
{
TextComponent = tx,
// The Subtemplates folder next to the executable
SearchPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Subtemplates")
})
{
mailMerge.IncludeTextMerging += MailMerge_IncludeTextMerging;
mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
}
tx.Save(out result, BinaryStreamType.InternalUnicodeFormat);
}
textControl1.Load(result, BinaryStreamType.InternalUnicodeFormat);
}
WPF has no Application.StartupPath; AppDomain.CurrentDomain.BaseDirectory is the folder the application runs from. The handler of IncludeTextMerging is shown next. Without one, each field includes its file from the search path.
Supply Subtemplates From Code
For each INCLUDETEXT field, MailMerge raises IncludeTextMerging, and your handler may hand over a document of its own. The file name then works as a key: here, the field with the file name legal-notice.docx receives a formatted sentence built in code:
private void MailMerge_IncludeTextMerging(object sender, MailMerge.IncludeTextMergingEventArgs e)
{
// Other files are included from the search path
if (!e.Filename.EndsWith("legal-notice.docx"))
{
return;
}
using (ServerTextControl notice = new ServerTextControl())
{
notice.Create();
notice.Text = "Prices include statutory VAT.";
notice.Select(0, notice.Text.Length);
notice.Selection.Italic = true;
byte[] data;
notice.Save(out data, BinaryStreamType.InternalUnicodeFormat);
e.IncludeTextDocument = data;
}
}
The code works as follows:
- Filename is the file name of the current field; IncludeTextField gives the field as an
IncludeTextobject. - The content is formatted in a second
ServerTextControl, which runs without a window, also in a WPF application. - IncludeTextDocument receives the document as a byte array in the internal TX Text Control format. Its reference page gives the type as
int, but the compiled property is abyte[], as used here.
An empty document leaves the subtemplate out; Conditional Content includes terms only for some records this way. The blog article MailMerge: Using IncludeText Fields to Inject Formatted Text fills several fields with generated text.
Next Steps
- Conditional Content: include a subtemplate only for some records.
- Template Design: plan templates, headers and footers.
- Merge Events: the other events of a merge.
- The blog article Using Sub-templates in Text Control Reporting: a page header shared as a subtemplate.