Skip to content

Message Composition

Norbert Bietsch edited this page Dec 30, 2018 · 7 revisions

Create a Data Source

In order to create a new message where {placeholders} shall be used, we need a data source. For this example it will be the following array:

Note: In all properties of a MailMergeMessage {placeholders} can be inserted, e.g. the subject, mailbox addresses, plain and HTML text, attachments, headers, ...

var variables = new[]
{
    new
    {
        FirstName = "John",
        LastName = "Specimen",
        Email = "john@example.com",
        Image = "john.jpg",
        Attachment = "EventJohn.pdf"
    },
    new
    {
        FirstName = "Mary",
        LastName = "Specimen",
        Email = "mary@example.com",
        Image = "mary.jpg",
        Attachment = "EventMary.pdf"
    }
};

Create a New MailMergeMessage

Next we create an instance of a MailMergeMessage. We want a personalized subject and we also add a MessageInfo with some background information about the message. The MessageInfo is optional but recommended.

var mmm = new MailMergeMessage("Event offer for you, {FirstName}")
{
    Info = new MessageInfo()
    {
        Id = 1,
        Category = "Customers",
        Description = "Event offer",
        Comments = "with personalized PDF attachment",
        Data = ""
    }
};

As you see, the MessageInfo also has property Data. For this example we will leave it blank.

MessageInfo.Data

MessageInfo.Data allows for storing "data hints". This could be e.g.

  • SQL statement for retrieving data from a database
  • A list of recipients
  • Other information for your data specific program logic
  • A CSharpScript for dynamic compilation with Roslyn

It can be virtually anything...

Mailbox Addresses

At least a From and a To mail address must be added.

mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, "whatever@example.com", Encoding.UTF8));
mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.ReturnReceiptTo, "whatever@example.com", Encoding.UTF8));
mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{FirstName} {LastName} <{Email}>", Encoding.UTF8));

Mailbox Address for Testing

There is a special mailbox address type which is helpful for testing:

mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.TestAddress, "whatever@example.com", Encoding.UTF8));

If MailAddressType.TestAddress will be added, all recipient mailbox addresses will be replaced with the test mailbox address. The display names will be left unchanged. Remember to remove MailAddressType.TestAddress after you've completed your tests.

Mailbox Address Encoding

Modern mail clients are able to deal with Encoding.UTF8 as the most widespread encoding for Unicode characters. Choose the Encoding parameter which fits best to your mail clients abilities.

Message Text Content

MailMergeMessage.PlainText and MailMergeMessage.HtmlText are fully independent from a technical viewpoint. Depending on the complexity of text formatting, special care should be given to the MailMergeMessage.PlainText for a good reading experience.

Plain Text

For simple scenarios the MailMergeMessage.PlainText can be automatically derived from the MailMergeMessage.HtmlText:

mmm.ConvertHtmlToPlainText();

If the built-in converter does not fulfill your requirments, you can use your own by implementing the interface IHtmlConverter.

mmm.ConvertHtmlToPlainText(mySpecialHtmlConverter);

HTML Text

An exciting matter about HTML text is, that you can enrich the text with images. MailMergeLib will embed the images into the mail message for you.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>Event offer for you, {FirstName}</title>
    <base href="file:///../"/>
</head>
<body>
    Dear {FirstName}:<br/>
    Today we have special event offer for you:<br/>
    <img src="{Image}" alt="Event Offer" width="100"/>
</body>
</html>
Considerations
  • Same as with plain text, HTML text may contain {placeholders} for variables.
  • For image files (and other attachment files) it is recommended to supply a base path to the location of the files, instead of using absolute paths for each image. This can be achieved by <base href="..."/>. However, it is better to code mmm.Config.FileBaseDirectory="path-to-files", because this path will be used for images and other attachment files.
  • The filename (Image is this example) may be a relative path.
  • The image HTML tag contains the placeholder {Image} which will be replaced with the variable value, e.g. "john.jpg". At the same time the image file will be added to the message as a "linked resource" and the image tag will change to <img src="cid:..." .
  • If you would include any <script> tags, these would be removed.
Increase compatibility of HTML messages with mail readers

What we should have in mind for HTML messages is to use

  • static, table-based layouts - no responsive laylouts
  • HTML tables which are nested as needed
  • templates with a width of 600-800 pixels
  • only simple, inline CSS

For inlining CSS there is a very good solution: PreMailer, available on NuGet, which works perfectly together with MailMergeLib.

In a very simple way - all HTML is included in MailMergeMessage.HtmlText - inlining CSS works like that:

var mmm = new MailMergeMessage(...); // create your mail message
mmm.HtmlText = "..."; // set your HTML part, using CSS
var result = PreMailer.MoveCssInline(mmm.HtmlText);
if (result.Warnings.Length > 0) { // process any warnings }
mmm.HtmlText = result.Html;  // set the processed HTML with CSS inlined.

In a future version, we are considering to implement a hook to PreMailer. They are also using AngleSharp, so we could avoid to parse the HTML part twice, and make templates work as well. Note: Currently PreMailer unfortunately does not come with a constructor taking an AngleSharp IHtmlDocument as an argument.

Adding Images for HTML Manually

Although it will be rarely necessary to add image files manually, but you can:

mmm.AddExternalInlineAttachment(new FileAttachment("filename.jpg", "cid-for-manual-inline-att"));

In this case, however, you'll also have to take care of the reference of the image tag:

<img src="cid:cid-for-manual-inline-att" />

Add Attachments to a MailMergeMessage

Note: The way how to use the attachments covered in this section applies for pure plain text, HTML text and mixed text messages.

File Attachments

Taking our sample data source, adding a PDF file works like that:

mmm.Config.FileBaseDirectory="path-to-files";
mmm.FileAttachments.Add(new FileAttachment("{Attachment}", "Event.pdf", "application/pdf"));
  • Use the FileBaseDirectory config setting instead of having absolute paths to files.
  • The constructor for a FileAttachment takes as parameters
    • the name of the file in the file system. The filename (Attachment is this example) may be a relative path.
    • the display name that the recipient of the mail message will see
    • the mime type (optional). MailMergeLib is able to detect the corrent mime type for the most common file types.

Stream Attachments

Adding a StreamAttachment instead of a FileAttachment:

var fs = new FileStream("{Attachment}", FileMode.Open);
mmm.StreamAttachments.Add(new StreamAttachment(fs, "Display Name", "application/pdf"));

String Attachments

Sometimes it can be handy to add an attachment created from a string.

mmm.StringAttachments.Add(new StringAttachment("Some text content...", "DisplayName.txt"));

Add Custom Headers

You could e.g. add a header which Microsoft Outlook honors as the expiration date of the message.

mmm.Headers.Add(HeaderId.Expires, Encoding.UTF8, DateTime.Now.AddDays(30).ToString("EEE, d MMM yyyy hh:mm:ss Z"));

All available headers are part of the HeaderId enumeration.

Save and Restore a MailMergeMessage

Mail messages can easily be stored and restored to/from XML. The XML can be serialized to a file system file, a stream or a string.

mmm.Serialize("path-to-file.xml", Encoding.UTF8);
// and later:
mmm = MailMergeMessage.Deserialize("path-to-file.xml", Encoding.UTF8);