100

I'm working on a site that will send out a significant number of emails. I want to set up both header and footer text, or maybe even templates to allow the users to easily edit these emails if they need to.

If I embed the HTML inside C# string literals, it's ugly and they would have to worry about escaping. Including flat files for the header and footer might work, but something about it just doesn't feel right.

What would be ideal what be to use a .ASPX page as a template somehow, then just tell my code to serve that page, and use the HTML returned for the email.

Is there a nice and easy way to do this? Is there a better way to go about solving this problem?

Updated:
I added an answer that enables you to use a standard .aspx page as the email template. Just replace all the variables like you normally would, use databinding, etc. Then just capture the output of the page, and voila! You have your HTML email!

UPDATED WITH CAVEAT!!!:
I was using the MailDefinition class on some aspx pages just fine, but when trying to use this class during a server process that was running, it failed. I believe it was because the MailDefinition.CreateMailMessage() method requires a valid control to reference, even though it doesn't always do something. Because of this, I would recommend my approach using an aspx page, or Mun's approach using an ascx page, which seems a little better.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
John B
  • 20,062
  • 35
  • 120
  • 170
  • Another solution would be to use [AlphaMail](http://comfirm.se) to create and send your emails using C# and the [Comlang](http://docs.amail.io/?section=comlang) template language. – Timothy E. Johansson Sep 27 '12 at 00:01
  • 1
    @JohnBubriski: I work around control problem you mentioned in "UPDATED WITH CAVEAT" using `new System.Web.UI.Control()` as in: `mailDefinition.CreateMailMessage("somebody@fake.com", iDictionaryReplacements, new System.Web.UI.Control())`. – Mike Grove aka Theophilus Aug 20 '14 at 15:50
  • Yeah, I have done that too, but given the advent of Razor, this is becoming less of a good idea. – John B Aug 21 '14 at 18:39

24 Answers24

75

There's a ton of answers already here, but I stumbled upon a great article about how to use Razor with email templating. Razor was pushed with ASP.NET MVC 3, but MVC is not required to use Razor. This is pretty slick processing of doing email templates

As the article identifies, "The best thing of Razor is that unlike its predecessor(webforms) it is not tied with the web environment, we can easily host it outside the web and use it as template engine for various purpose. "

Generating HTML emails with RazorEngine - Part 01 - Introduction

Leveraging Razor Templates Outside of ASP.NET: They’re Not Just for HTML Anymore!

Smarter email templates in ASP.NET with RazorEngine

Similar Stackoverflow QA

Templating using new RazorEngine API

Using Razor without MVC

Is it possible to use Razor View Engine outside asp.net

Community
  • 1
  • 1
Mike Barlow - BarDev
  • 11,087
  • 17
  • 62
  • 83
  • 1
    +1 but be careful if users are providing the templates, as they can run C# code from the template, giving them alot more power in your system than you would probably want. – AaronLS Mar 07 '13 at 21:54
  • What do you think about Security. Using this templating engine makes it possible to maybe format the whole file system. I like the engine, but that forces me to have a look at other engines. – der_chirurg Sep 24 '13 at 09:55
55

You might also want to try loading a control, and then rendering it to a string and setting that as the HTML Body:

// Declare stringbuilder to render control to
StringBuilder sb = new StringBuilder();

// Load the control
UserControl ctrl = (UserControl) LoadControl("~/Controls/UserControl.ascx");

// Do stuff with ctrl here

// Render the control into the stringbuilder
StringWriter sw = new StringWriter(sb);
Html32TextWriter htw = new Html32TextWriter(sw);
ctrl.RenderControl(htw);

// Get full body text
string body = sb.ToString();

You could then construct your email as usual:

MailMessage message = new MailMessage();
message.From = new MailAddress("from@email.com", "from name");
message.Subject = "Email Subject";
message.Body = body;
message.BodyEncoding = Encoding.ASCII;
message.IsBodyHtml = true;

SmtpClient smtp = new SmtpClient("server");
smtp.Send(message);

You user control could contain other controls, such as a header and footer, and also take advantage of functionality such as data binding.

Mun
  • 14,098
  • 11
  • 59
  • 83
  • I somehow missed this answer the first time around... nice one. Similar to my solution, but with an ascx instead of an aspx. I still think aspx would be better, since it would offer a complete page, instead of a control, but that's just what I think. – John B Mar 26 '09 at 19:26
  • Yep, you could use either solution... They work in the same way. One benefit of this approach is consistency. For example, you could show a user an order summary and include exactly the same thing in the confirmation email by reusing the same control. – Mun Mar 26 '09 at 21:19
  • Minor point but you're missing a line to declare a StringBuilder in the first code block. – Kirschstein Oct 05 '09 at 13:38
  • 9
    The example doesn't explain where the code resides in, is it a page?, because LoadControl is a page/control method. – Shrage Smilowitz May 31 '10 at 18:11
  • @Mun, You load the usercontrol into a variable called ctrl, and you never reference it again in your code. How is this supposed to work? – The Muffin Man May 09 '11 at 22:06
  • @Nick: There was a typo in the code, now fixed. The RenderControl method is used to render the control to the HtmlTextWriter, where it can then be used as required. – Mun May 10 '11 at 00:02
  • @Mun, I see. On my .ascx page I have a listview on it, but it appears that the listview isn't creating my content before being loaded. Do you have any suggestions? – The Muffin Man May 10 '11 at 00:37
  • @Nick: Are you binding the ListView in your Page_Load event? It's a little hard to guess what the problem is without seeing the code, but it sounds like the way the ListView is being bound might need some modification. – Mun May 10 '11 at 03:58
  • @Mun, see the answer to my question I asked earlier: http://stackoverflow.com/questions/5944076/using-loadcontrol-on-pages-with-server-controls – The Muffin Man May 10 '11 at 04:32
  • It can be further enhanced. Add a public method to user control. Type cast the user control to its class name, instead of generic UserControl. And now we can call this public method to act like Page_Load for user control, by calling it immediately after loading the UserControl. This helps in passing parameters to UserControl easily as well. – iMatoria Aug 07 '14 at 20:11
35

You could try the MailDefinition class

TechSavvySam
  • 1,382
  • 16
  • 28
John Sheehan
  • 77,456
  • 30
  • 160
  • 194
  • 4
    I just want to point out that this is good for basic emails, but not anything complex. The MailDefinition class does not support databinding. The only thing it really does is offer string replacements. Although, its also built into the Membership Account Creation Wizard. – John B Mar 24 '09 at 16:43
  • 4
    The MailDefinition class must get a Control to render templated content.. Not so good. – Yuki Aug 17 '11 at 07:54
17

If you want to pass parameters like user names, product names, ... etc. you can use open source template engine NVelocity to produce your final email / HTML's.

An example of NVelocity template (MailTemplate.vm) :

A sample email template by <b>$name</b>.
<br />

Foreach example :
<br />    
#foreach ($item in $itemList)

[Date: $item.Date] Name: $item.Name, Value: $itemValue.Value
<br /><br />

#end

Generating mail body by MailTemplate.vm in your application :

VelocityContext context = new VelocityContext();
context.Put("name", "ScarletGarden");
context.Put("itemList", itemList);

StringWriter writer = new StringWriter();

Velocity.MergeTemplate("MailTemplate.vm", context, writer);

string mailBody = writer.GetStringBuilder().ToString();

The result mail body is :

A sample email template by ScarletGarden.

Foreach example :

[Date: 12.02.2009] Name: Item 1, Value: 09

[Date: 21.02.2009] Name: Item 4, Value: 52

[Date: 01.03.2009] Name: Item 2, Value: 21

[Date: 23.03.2009] Name: Item 6, Value: 24

For editing the templates, maybe you can use FCKEditor and save your templates to files.

Canavar
  • 47,715
  • 17
  • 91
  • 122
7

Mail.dll email component includes email template engine:

Here's the syntax overview:

<html>
<body>
Hi {FirstName} {LastName},

Here are your orders: 
{foreach Orders}
    Order '{Name}' sent to <strong>{Street}</strong>. 
{end}

</body>
</html>

And the code that loads the template, fills data from c# object and sends an email:

Mail.Html(Template
              .FromFile("template.txt")
              .DataFrom(_contact)
              .Render())
    .Text("This is text version of the message.")
    .From(new MailBox("alice@mail.com", "Alice"))
    .To(new MailBox("bob@mail.com", "Bob"))
    .Subject("Your order")
    .UsingNewSmtp()
    .WithCredentials("alice@mail.com", "password")
    .Server("mail.com")
    .WithSSL()
    .Send();

You can get more info on email template engine blog post.

Or just download Mail.dll email component and give it a try.

Please note that this is a commercial product I've created.

Pawel Lesnikowski
  • 6,264
  • 4
  • 38
  • 42
6

If flexibility is one of your prerequisites, XSLT might be a good choice, which is completely supported by .NET framework and you would be able to even let the user edit those files. This article (http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/) might be useful for a start (msdn has more info about it). As said by ScarletGarden NVelocity is another good choice but I do prefer XSLT for its " built-in" .NET framework support and platform agnostic.

Everton
  • 223
  • 3
  • 9
  • I had never thought of this before, but after trying lots of other methods I found this worked great in combination with adding the `IXmlSerializable` interface to my classes. In just a few lines I can have my class turn in to an email. – cjbarth Jan 25 '13 at 20:04
  • Urgh, I've dreamt nightmares about XSLT. Probably the most non-intuitive programming/markup language I've worked with. And impossible to maintain for others and even for yourself 1 month after you first coded your XSLT. – PussInBoots Feb 08 '14 at 14:48
5

I think you could also do something like this:

Create and .aspx page, and put this at the end of the OnLoad method, or call it manually.

    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
    this.Render(htmlTW);

I'm not sure if there are any potential issues with this, but it looks like it would work. This way, you could use a full featured .aspx page, instead of the MailDefinition class which only supports Text replacements.

John B
  • 20,062
  • 35
  • 120
  • 170
  • While the MailDefinition class is a good start, its a bit rudimentary. This method should support a lot more features like databinding, and maybe even tracing. Any thoughts on this, or potential gotchas? – John B Mar 24 '09 at 16:38
  • Great! Did you have any issues with it? – John B Mar 24 '09 at 16:46
  • So you're going to let your users edit the .aspx files when they need to make changes to the mail template? I'd call that a potential issue. – Bryan Mar 27 '09 at 04:45
  • I wouldn't think so, at least, no more of a risk than other templates they could edit. Granted, if they knew what they were doing, they could cause harm, but in this case at least, its unlikely. It wouldn't be a complex .aspx page, more of a template with placeholders. – John B Mar 27 '09 at 12:44
  • It's been a while, I know, but do you recall your final solution? I wasn't able to get this particular approach to work with a `Page`, at least when using a generic extension method for the rendering. Thus I switched to `UserControl`; see my answer below. So far it seems to be working well... I'd be interested to learn how you worked it out at the time. – InteXX Sep 01 '14 at 00:34
  • Up voting this answer because you answer specifically how to do it with `.aspx` pages, as that might help some one else who is using those kind of templates. – ahong Aug 29 '19 at 16:36
4

Here is one more alternative that uses XSL transformations for more complex email templates: Sending HTML-based email from .NET applications.

Alek Davis
  • 10,628
  • 2
  • 41
  • 53
  • 2
    Like the link. Thanks! My brain started turning and realized I could take it 1 step further, and have an XSLT template that takes an XML Serializable object, or WCF Data Contract right into html-email format. Suddenly I'd have 'strong-typed' email templates through actual serializable classes! – CodingWithSpike Feb 17 '11 at 21:47
4

Sure you can create an html template and I would recommend also a text template. In the template you can just put [BODY] in the place where the body would be placed and then you can just read in the template and replace the body with the new content. You can send the email using .Nets Mail Class. You just have to loop through the sending of the email to all recipients after you create the email initially. Worked like a charm for me.

using System.Net.Mail;

// Email content
string HTMLTemplatePath = @"path";
string TextTemplatePath = @"path";
string HTMLBody = "";
string TextBody = "";

HTMLBody = File.ReadAllText(HTMLTemplatePath);
TextBody = File.ReadAllText(TextTemplatePath);

HTMLBody = HTMLBody.Replace(["[BODY]", content);
TextBody = HTMLBody.Replace(["[BODY]", content);

// Create email code
MailMessage m = new MailMessage();

m.From = new MailAddress("address@gmail.com", "display name");
m.To.Add("address@gmail.com");
m.Subject = "subject";

AlternateView plain = AlternateView.CreateAlternateViewFromString(_EmailBody + text, new System.Net.Mime.ContentType("text/plain"));
AlternateView html = AlternateView.CreateAlternateViewFromString(_EmailBody + body, new System.Net.Mime.ContentType("text/html"));
mail.AlternateViews.Add(plain);
mail.AlternateViews.Add(html);

SmtpClient smtp = new SmtpClient("server");
smtp.Send(m);
Josh Mein
  • 28,107
  • 15
  • 76
  • 87
  • You can cut out the StreamReader stuff and replace with File.ReadAllText(path) – John Sheehan Mar 06 '09 at 21:09
  • This is a good start, but only provides functionality for a header and footer. This doesn't really help with the body itself. – John B Mar 24 '09 at 16:44
  • The body all you have to do is enter the body content that is desired in to the HTMLBody and TextBody Fields or you can of course store them in files as well – Josh Mein Mar 24 '09 at 16:56
2

Be careful when doing this, SPAM filters seem to block ASP.net generated html, apparently because of ViewState, so if you are going to do this make sure the Html produced is clean.

I personally would look into using Asp.net MVC to achieve your desired results. or NVelocity is quite good at this

danswain
  • 4,171
  • 5
  • 37
  • 43
1

DotLiquid is another option. You specify values from a class model as {{ user.name }} and then at runtime you provide the data in that class, and the template with the markup, and it will merge the values in for you. It is similar to using the Razor templating engine in many ways. It supports more complex things like loops and various function like ToUpper. The nice thing is these are "safe" so that user's who create the templates can't crash your system or write unsafe code like you would in razor: http://dotliquidmarkup.org/try-online

AaronLS
  • 37,329
  • 20
  • 143
  • 202
1

Note that the aspx and ascx solutions require a current HttpContext, so cannot be used asynchronously (eg in threads) without a lot of work.

Rosco
  • 11
  • 1
1

I think the easy answer is MvcMailer. It s NuGet package that lets you use your favorite view engine to generate emails. See the NuGet package here and the project documentation

Hope it helps!

Sohan
  • 3,757
  • 2
  • 24
  • 24
1

Just throwing the library I'm using into the mix: https://github.com/lukencode/FluentEmail

It renders emails using RazorLight, uses the fluent style to build emails, and supports multiple senders out of the box. It comes with extension methods for ASP.NET DI too. Simple to use, little setup, with plain text and HTML support.

ahong
  • 1,041
  • 2
  • 10
  • 22
1

What would be ideal what be to use a .ASPX page as a template somehow, then just tell my code to serve that page, and use the HTML returned for the email.

You could easily just construct a WebRequest to hit an ASPX page and get the resultant HTML. With a little more work, you can probably get it done without the WebRequest. A PageParser and a Response.Filter would allow you to run the page and capture the output...though there may be some more elegant ways.

Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
1

i had a similar requirement on 1 of the projects where you had to send huge number of emails each day, and the client wanted complete control over html templates for different types of emails.

due to the large number of emails to be sent, performance was a primary concern.

what we came up with was static content in sql server where you save entire html template mark up (along with place holders, like [UserFirstName], [UserLastName] which are replaced with real data at run time) for different types of emails

then we loaded this data in asp.net cache - so we dont read the html templates over and over again - but only when they are actually changed

we gave the client a WYSIWYG editor to modify these templates via a admin web form. whenever updates were made, we reset asp.net cache.

and then we had a seperate table for email logs - where every email to be sent was logged. this table had fields called emailType, emailSent and numberOfTries.

we simply ran a job every 5 minutes for important email types (like new member sign up, forgot password) which need to be sent asap

we ran another job every 15 minutes for less important email types (like promotion email, news email, etc)

this way you dont block your server sending non stop emails and you process mails in batch. once an email is sent you set the emailSent field to 1.

Raj
  • 6,810
  • 6
  • 48
  • 56
0

Here is a simple way using the WebClient class:

public static string GetHTMLBody(string url)
{
    string htmlBody;

    using (WebClient client = new WebClient ())
    {
        htmlBody = client.DownloadString(url);
    }

    return htmlBody;
}

Then just call it like this:

string url = "http://www.yourwebsite.com";
message.Body = GetHTMLBody(url);

Of course, your CSS will need to be in-lined in order to show the styles of the webpage in the most email clients (such as Outlook). If your e-mail displays dynamic content (ex. Customer Name), then I would recommend using QueryStrings on your website to populate the data. (ex. http://www.yourwebsite.com?CustomerName=Bob)

Gaff
  • 5,507
  • 3
  • 38
  • 49
  • Neat, though I think most of the other answers do this without making a web request back to the site i.e. having to host the email body on your site. – Rup Oct 19 '12 at 14:37
  • @Rup Understandable, but keep in mind a lot of times people want to see a "Web Version" of the e-mail anyway. This solution works perfect for that scenario. – Gaff Oct 19 '12 at 14:49
0

Similar to Canavar's answer, but instead of NVelocity, I always use "StringTemplate" which I load the template from a configuration file, or load an external file using File.ReadAllText() and set the values.

It's a Java project but the C# port is solid and I've used it in several projects (just used it for email templating using the template in an external file).

Alternatives are always good.

Bryan Bailliache
  • 1,173
  • 9
  • 10
0

@bardev provides a good solution, but unfortunately it's not ideal in all cases. Mine was one of them.

I'm using WebForms in a Website (I swear I'll never use a Website again--what a PITA) in VS 2013.

I tried the Razor suggestion, but mine being a Website I didn't get the all-important IntelliSense that the IDE delivers in an MVC project. I also like to use the designer for my templates--a perfect spot for a UserControl.

Nix on Razor again.

So I came up with this little framework instead (hat tips to @mun for UserControl and @imatoria for Strong Typing). Just about the only potential trouble spot I can see is that you have to be careful to keep your .ASCX filename in sync with its class name. If you stray, you'll get a runtime error.

FWIW: In my testing at least the RenderControl() call doesn't like a Page control, so I went with UserControl.

I'm pretty sure I've included everything here; let me know if I left something out.

HTH

Usage:

Partial Class Purchase
  Inherits UserControl

  Private Sub SendReceipt()
    Dim oTemplate As MailTemplates.PurchaseReceipt

    oTemplate = MailTemplates.Templates.PurchaseReceipt(Me)
    oTemplate.Name = "James Bond"
    oTemplate.OrderTotal = 3500000
    oTemplate.OrderDescription = "Q-Stuff"
    oTemplate.InjectCss("PurchaseReceipt")

    Utils.SendMail("{0} <james.bond@mi6.co.uk>".ToFormat(oTemplate.Name), "Purchase Receipt", oTemplate.ToHtml)
  End Sub
End Class

Base Class:

Namespace MailTemplates
  Public MustInherit Class BaseTemplate
    Inherits UserControl

    Public Shared Function GetTemplate(Caller As TemplateControl, Template As Type) As BaseTemplate
      Return Caller.LoadControl("~/MailTemplates/{0}.ascx".ToFormat(Template.Name))
    End Function



    Public Sub InjectCss(FileName As String)
      If Me.Styler IsNot Nothing Then
        Me.Styler.Controls.Add(New Controls.Styler(FileName))
      End If
    End Sub



    Private ReadOnly Property Styler As PlaceHolder
      Get
        If _Styler Is Nothing Then
          _Styler = Me.FindNestedControl(GetType(PlaceHolder))
        End If

        Return _Styler
      End Get
    End Property
    Private _Styler As PlaceHolder
  End Class
End Namespace

"Factory" Class:

Namespace MailTemplates
  Public Class Templates
    Public Shared ReadOnly Property PurchaseReceipt(Caller As TemplateControl) As PurchaseReceipt
      Get
        Return BaseTemplate.GetTemplate(Caller, GetType(PurchaseReceipt))
      End Get
    End Property
  End Class
End Namespace

Template Class:

Namespace MailTemplates
  Public MustInherit Class PurchaseReceipt
    Inherits BaseTemplate

    Public MustOverride WriteOnly Property Name As String
    Public MustOverride WriteOnly Property OrderTotal As Decimal
    Public MustOverride WriteOnly Property OrderDescription As String
  End Class
End Namespace

ASCX Header:

<%@ Control Language="VB" ClassName="_Header" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<!--
  See https://www.campaignmonitor.com/blog/post/3317/ for discussion of DocType in HTML Email
-->

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <asp:PlaceHolder ID="plcStyler" runat="server"></asp:PlaceHolder>
</head>
<body>

ASCX Footer:

<%@ Control Language="VB" ClassName="_Footer" %>

</body>
</html>

ASCX Template:

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="PurchaseReceipt.ascx.vb" Inherits="PurchaseReceipt" %>

<%@ Register Src="_Header.ascx" TagName="Header" TagPrefix="uc" %>
<%@ Register Src="_Footer.ascx" TagName="Footer" TagPrefix="uc" %>

<uc:Header ID="ctlHeader" runat="server" />

  <p>Name: <asp:Label ID="lblName" runat="server"></asp:Label></p>
  <p>Order Total: <asp:Label ID="lblOrderTotal" runat="server"></asp:Label></p>
  <p>Order Description: <asp:Label ID="lblOrderDescription" runat="server"></asp:Label></p>

<uc:Footer ID="ctlFooter" runat="server" />

ASCX Template CodeFile:

Partial Class PurchaseReceipt
  Inherits MailTemplates.PurchaseReceipt

  Public Overrides WriteOnly Property Name As String
    Set(Value As String)
      lblName.Text = Value
    End Set
  End Property



  Public Overrides WriteOnly Property OrderTotal As Decimal
    Set(Value As Boolean)
      lblOrderTotal.Text = Value
    End Set
  End Property



  Public Overrides WriteOnly Property OrderDescription As Decimal
    Set(Value As Boolean)
      lblOrderDescription.Text = Value
    End Set
  End Property
End Class

Helpers:

'
' FindNestedControl helpers based on tip by @andleer
' at http://stackoverflow.com/questions/619449/
'

Public Module Helpers
  <Extension>
  Public Function AllControls(Control As Control) As List(Of Control)
    Return Control.Controls.Flatten
  End Function



  <Extension>
  Public Function FindNestedControl(Control As Control, Id As String) As Control
    Return Control.Controls.Flatten(Function(C) C.ID = Id).SingleOrDefault
  End Function



  <Extension>
  Public Function FindNestedControl(Control As Control, Type As Type) As Control
    Return Control.Controls.Flatten(Function(C) C.GetType = Type).SingleOrDefault
  End Function



  <Extension>
  Public Function Flatten(Controls As ControlCollection) As List(Of Control)
    Flatten = New List(Of Control)

    Controls.Traverse(Sub(Control) Flatten.Add(Control))
  End Function


  <Extension>
  Public Function Flatten(Controls As ControlCollection, Predicate As Func(Of Control, Boolean)) As List(Of Control)
    Flatten = New List(Of Control)

    Controls.Traverse(Sub(Control)
                        If Predicate(Control) Then
                          Flatten.Add(Control)
                        End If
                      End Sub)
  End Function



  <Extension>
  Public Sub Traverse(Controls As ControlCollection, Action As Action(Of Control))
    Controls.Cast(Of Control).ToList.ForEach(Sub(Control As Control)
                                               Action(Control)

                                               If Control.HasControls Then
                                                 Control.Controls.Traverse(Action)
                                               End If
                                             End Sub)
  End Sub



  <Extension()>
  Public Function ToFormat(Template As String, ParamArray Values As Object()) As String
    Return String.Format(Template, Values)
  End Function



  <Extension()>
  Public Function ToHtml(Control As Control) As String
    Dim oSb As StringBuilder

    oSb = New StringBuilder

    Using oSw As New StringWriter(oSb)
      Using oTw As New HtmlTextWriter(oSw)
        Control.RenderControl(oTw)
        Return oSb.ToString
      End Using
    End Using
  End Function
End Module



Namespace Controls
  Public Class Styler
    Inherits LiteralControl

    Public Sub New(FileName As String)
      Dim _
        sFileName,
        sFilePath As String

      sFileName = Path.GetFileNameWithoutExtension(FileName)
      sFilePath = HttpContext.Current.Server.MapPath("~/Styles/{0}.css".ToFormat(sFileName))

      If File.Exists(sFilePath) Then
        Me.Text = "{0}<style type=""text/css"">{0}{1}</style>{0}".ToFormat(vbCrLf, File.ReadAllText(sFilePath))
      Else
        Me.Text = String.Empty
      End If
    End Sub
  End Class
End Namespace



Public Class Utils
  Public Shared Sub SendMail(Recipient As MailAddress, Subject As String, HtmlBody As String)
    Using oMessage As New MailMessage
      oMessage.To.Add(Recipient)
      oMessage.IsBodyHtml = True
      oMessage.Subject = Subject.Trim
      oMessage.Body = HtmlBody.Trim

      Using oClient As New SmtpClient
        oClient.Send(oMessage)
      End Using
    End Using
  End Sub
End Class
InteXX
  • 6,135
  • 6
  • 43
  • 80
0

If you are able to allow the ASPNET and associated users permission to read & write a file, you can easily use an HTML file with standard String.Format() placeholders ({0}, {1:C}, etc.) to accomplish this.

Merely read in the file, as a string, using classes from the System.IO namespace. Once you have that string, pass it as the first argument to String.Format(), and provide the parameters.

Keep that string around, and use it as the body of the e-mail, and you're essentially done. We do this on dozens of (admittedly small) sites today, and have had no issues.

I should note that this works best if (a) you're not sending zillions of e-mails at a time, (b) you're not personalizing each e-mail (otherwise you eat up a ton of strings) and (c) the HTML file itself is relatively small.

John Rudy
  • 37,282
  • 14
  • 64
  • 100
0

Set the set the Email Message IsBodyHtml = true

Take your object that contains your email contents Serialize the object and use xml/xslt to generate the html content.

If you want to do AlternateViews do the same thing that jmein only use a different xslt template to create the plain text content.

one of the major advantages to this is if you want to change your layout all you have to do update the xslt template.

Bob The Janitor
  • 20,292
  • 10
  • 49
  • 72
0

Look at SubSonic (www.subsonicproject.com). They're doing exactly this to generate code - the template is standard ASPX, and it outputs c#. The same method would be reusable for your scenario.

Jerod Venema
  • 44,124
  • 5
  • 66
  • 109
0

I'd use a templating library like TemplateMachine. this allows you mostly put your email template together with normal text and then use rules to inject/replace values as necessary. Very similar to ERB in Ruby. This allows you to separate the generation of the mail content without tying you too heavily to something like ASPX etc. then once the content is generated with this, you can email away.

MikeJ
  • 14,430
  • 21
  • 71
  • 87
0

I like Raj's answer. Programs like ListManager & frameworks like DNN do similar things, and if easy editing by non-technical users is required, WYSIWYG editors to modify HTML stored in SQL is a mostly easy, straightforward way to go and can easily accommodate editing headers independently from footers, etc, as well as using tokens to dynamically insert values.

One thing to keep in mind if using the above method (or any, really) is to be strict and careful about which types of styling and tags you allow the editors to insert. If you think browsers are finicky, just wait until you see how differently email clients render the same thing...

Nick
  • 1,708
  • 14
  • 18