Mail Merge Placeholders

SpiceLogic WinForms HTML Editor mail merge placeholder dropdown toolbar showing PlaceholderField entries ready for insertion at the caret

Mail merge in the WinForms HTML Editor is driven by a small, host-app-owned list of PlaceholderField objects. Each field describes a single merge token (for example, First Name with the underlying token {{FirstName}}). The editor exposes the list, an insertion API, and a notification event through the IMailMergeService available on editor.Content.MailMerge. There is also an opt-in toolbar (ShowPlaceholderToolbar) for trial users who want to see the feature working without wiring their own picker.

The host application is the source of truth for which fields are available. The control never inspects your data model; it simply emits the token you provide at the caret position and raises an event so you can log or audit each insertion.

Providing the fields

SpiceLogic WinForms HTML Editor Content.MailMerge PlaceholderFields list populated at runtime with First Name, Last Name, Company and Invoice Total entries

Assign the list of placeholders to PlaceholderFields on the mail-merge service. Each PlaceholderField is constructed with a displayName (the human-readable label shown in pickers) and a token (the raw text that gets dropped into the document). Both arguments are required; the constructor throws ArgumentException if either is null or whitespace.

using System.Collections.Generic;
using SpiceLogic.HtmlEditor.Abstractions.Entities.MailMerge;

htmlEditor1.Content.MailMerge.PlaceholderFields = new List<PlaceholderField>
{
    new PlaceholderField("First Name",    "{{FirstName}}"),
    new PlaceholderField("Last Name",     "{{LastName}}"),
    new PlaceholderField("Company",       "{{Company}}"),
    new PlaceholderField("Invoice Total", "{{InvoiceTotal}}")
};

Two read-only properties expose what you stored: DisplayName and Token. The class is sealed and immutable, so once a PlaceholderField is constructed its contents cannot change - replace the entry in the list instead. The list itself can be mutated or reassigned at any time; the built-in toolbar picks up changes on its next rebuild (call RebindFields() on the toolbar if you want an immediate refresh).

Inserting programmatically

SpiceLogic WinForms HTML Editor Content.MailMerge.InsertPlaceholder writing a non-editable PlaceholderField token at the active caret position at runtime

If you prefer your own field picker (a context menu, a sidebar list, a slash-command palette), call InsertPlaceholder directly. The editor wraps the token in a styled, non-editable inline span at the current caret position so end-users cannot accidentally split or partially delete the token text:

private void InsertFirstName_Click(object sender, EventArgs e)
{
    PlaceholderField firstName = htmlEditor1.Content.MailMerge
        .PlaceholderFields.First(f => f.DisplayName == "First Name");

    htmlEditor1.Content.MailMerge.InsertPlaceholder(firstName);
}

Reacting to insertion

SpiceLogic WinForms HTML Editor PlaceholderInserted event handler hit in the Visual Studio debugger showing PlaceholderInsertedEventArgs Field and InsertedHtml values

Each successful insertion raises PlaceholderInserted. The PlaceholderInsertedEventArgs carries two read-only properties: Field (the PlaceholderField that was inserted) and InsertedHtml (the exact HTML fragment the editor emitted into the document). Use this to drive analytics, log merge-field usage, update a "fields currently used in this draft" sidebar, or surface a confirmation toast:

htmlEditor1.Content.MailMerge.PlaceholderInserted += (sender, args) =>
{
    Debug.WriteLine($"Inserted {args.Field.DisplayName} -> {args.Field.Token}");
    Debug.WriteLine($"HTML emitted: {args.InsertedHtml}");
};

Caveats

SpiceLogic WinForms HTML Editor mail merge InsertPlaceholder requires WysiwygDesign mode and validates PlaceholderField DisplayName and Token inputs

WYSIWYG mode only. InsertPlaceholder is silently ignored when the editor is in HTML source mode. The editor must be in WysiwygDesign mode for an insertion to occur. Switch the mode programmatically (or restrict your picker UI to the design view) before calling the method if your host UI might be in source view.

Validation. PlaceholderField requires non-empty DisplayName and Token values - the constructor throws ArgumentException on null or whitespace input, so feed it sanitised data from your model.

Token resolution is your responsibility. The editor only inserts the token verbatim. Replacing {{FirstName}} with the actual customer name happens at send time in your own merge pipeline.

Last updated on May 12, 2026