Mail Merge Placeholders
The WPF HTML Editor includes a first-class mail-merge surface so your end users can drop placeholder tokens such as {{FirstName}} or {{InvoiceNumber}} into the document at the current caret position. Your application then resolves those tokens at send-time. The placeholders are emitted as styled, non-editable inline spans so users cannot accidentally type into the middle of a token and break it.
The mail-merge entry point is the IMailMergeService exposed on the editor's content surface as editor.Content.MailMerge. It exposes the field list, an InsertPlaceholder method, and a PlaceholderInserted event for analytics or sidebar tracking.

Configuring the placeholder fields
Each placeholder is a PlaceholderField with two values: a DisplayName shown in the picker UI and a Token string that gets emitted into the document. Set or mutate the PlaceholderFields list before the user opens any picker; the list is safe to mutate at runtime and the built-in toolbar picks up changes on the next rebuild.
using System.Collections.Generic; using SpiceLogic.HtmlEditor.Abstractions.Entities.MailMerge; using SpiceLogic.HtmlEditor.WPF; private void Window_Loaded(object sender, RoutedEventArgs e) { Editor.Content.MailMerge.PlaceholderFields = new List<PlaceholderField> { new PlaceholderField("First Name", "{{FirstName}}"), new PlaceholderField("Last Name", "{{LastName}}"), new PlaceholderField("Order Total", "{{OrderTotal}}") }; }

Inserting at the caret
Call InsertPlaceholder with the chosen field. The editor must be in WysiwygDesign mode for the call to take effect — calls made while the editor is in HTML-source mode are silently ignored, since there is no live caret to insert at.
using SpiceLogic.HtmlEditor.Abstractions.Entities.MailMerge; private void InsertFirstNameButton_Click(object sender, RoutedEventArgs e) { PlaceholderField firstName = Editor.Content.MailMerge.PlaceholderFields[0]; Editor.Content.MailMerge.InsertPlaceholder(firstName); }
If you want a built-in field picker instead of building your own, set Editor.ShowPlaceholderToolbar = true. That reveals a small toolbar row above the editor body with a dropdown bound to the configured fields and an Insert button that calls InsertPlaceholder internally. The opt-in toolbar is hidden by default. After mutating the field list at runtime, call Editor.PlaceholderToolbar.RebindFields() so the dropdown reflects the new entries.

Reacting to insertion
The PlaceholderInserted event fires after a placeholder span has been added to the document. The PlaceholderInsertedEventArgs instance carries the Field that was inserted plus the exact InsertedHtml markup. Use this hook to log merge-field usage, drive analytics, or update a sidebar that lists which fields the user has already placed in the current draft.
Editor.Content.MailMerge.PlaceholderInserted += (s, e) => { Console.WriteLine($"Inserted {e.Field.DisplayName} => {e.Field.Token}"); Console.WriteLine($"Markup: {e.InsertedHtml}"); };

Caveats
Mail-merge insertion is a WYSIWYG-design-only operation. The editor uses the live caret in design mode to compute the insertion point; in source mode there is no caret in the rendered DOM, so InsertPlaceholder returns without doing anything. If you build a custom toolbar, disable your insert affordance whenever the editor is not in design mode so users get clear visual feedback rather than an apparently silent click.
Both DisplayName and Token are required: the PlaceholderField constructor throws ArgumentException if either is null, empty, or whitespace. The token string is treated as opaque by the editor — choose any delimiter convention that makes round-tripping unambiguous on your server (the most common are {{Name}} or [%Name%]).
Full example
Putting it all together, here is a minimal WPF window that registers three fields, opts into the built-in toolbar, and logs each insertion:
using System; using System.Collections.Generic; using System.Windows; using SpiceLogic.HtmlEditor.Abstractions.Entities.MailMerge; namespace WpfMailMergeDemo; public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { IList<PlaceholderField> fields = new List<PlaceholderField> { new PlaceholderField("First Name", "{{FirstName}}"), new PlaceholderField("Last Name", "{{LastName}}"), new PlaceholderField("Order Total", "{{OrderTotal}}") }; Editor.Content.MailMerge.PlaceholderFields = fields; Editor.ShowPlaceholderToolbar = true; Editor.PlaceholderToolbar?.RebindFields(); Editor.Content.MailMerge.PlaceholderInserted += (s, args) => Console.WriteLine($"Inserted: {args.Field}"); } }
The companion XAML is the same minimal hosting markup you would use for any WpfHtmlEditor instance — declare the namespace prefix and drop the control into a layout container with x:Name="Editor".