How To Send HTML Email from a LightSwitch Application
A while back Paul Patterson wrote an awesome step-by-step blog post based on a forum question on how to send an automated email using System.Net.Mail from LightSwitch. If you missed it here it is:
Microsoft LightSwitch – Send an Email from LightSwitch
This is a great solution if you want to send email in response to some event happening in the data update pipeline on the server side. In his example he shows how to send a simple text-based email using an SMTP server in response to a new appointment being inserted into the database. In this post I want to show you how you can create richer HTML mails from data and send them via SMTP. I also want to present a client-side solution that creates an email using the Outlook client which allows the user to see and modify the email before it is sent.
Sending Email via SMTP
As Paul explained, all you need to do to send an SMTP email from the LightSwitch middle-tier (server side) is switch to File View on the Solution Explorer and add a class to the Server project. Ideally you’ll want to put the class in the UserCode folder to keep things organized.
TIP: If you don’t see a UserCode folder that means you haven’t written any server rules yet. To generate this folder just go back and select any entity in the designer, drop down the “Write Code” button on the top right, and select one of the server methods like entity_Inserted.
The basic code to send an email is simple. You just need to specify the SMTP server, user id, password and port. TIP: If you only know the user ID and password then you can try using Outlook 2010 to get the rest of the info for you automatically.
Notice in my SMTPMailHelper class I’m doing a quick check to see whether the body parameter contains HTML and if so, I set the appropriate mail property.
Imports System.Net
Imports System.Net.Mail
Public Class SMTPMailHelper
Const SMTPServer As String = "smtp.mydomain.com"
Const SMTPUserId As String = "myemail@mydomain.com"
Const SMTPPassword As String = "mypassword"
Const SMTPPort As Integer = 25
Public Shared Sub SendMail(ByVal sendFrom As String,
ByVal sendTo As String,
ByVal subject As String,
ByVal body As String)
Dim fromAddress = New MailAddress(sendFrom)
Dim toAddress = New MailAddress(sendTo)
Dim mail As New MailMessage
With mail
.From = fromAddress
.To.Add(toAddress)
.Subject = subject
If body.ToLower.Contains("<html>") Then
.IsBodyHtml = True
End If
.Body = body
End With
Dim smtp As New SmtpClient(SMTPServer, SMTPPort)
smtp.Credentials = New NetworkCredential(SMTPUserId, SMTPPassword)
smtp.Send(mail)
End Sub
End Class
Creating HTML from Entity Data
Now that we have the code to send an email I want to show you how we can quickly generate HTML from entity data using Visual Basic’s XML literals. (I love XML literals and have written about them many times before.) If you are new to XML literals I suggest starting with this article and this video. To use XML literals you need to make sure you have an assembly reference to System.Core, System.Xml, and System.Xml.Linq.
What I want to do is create an HTML email invoice for an Order entity that has children Order_Details. First I’ve made my life simpler by adding computed properties onto the Order_Details and Order entities that calculate line item and order totals respectively. The code for these computed properties is as follows:
Public Class Order_Detail
Private Sub LineTotal_Compute(ByRef result As Decimal)
' Calculate the line item total for each Order_Detail
result = (Me.Quantity * Me.UnitPrice) * (1 - Me.Discount)
End Sub
End Class
Public Class Order
Private Sub OrderTotal_Compute(ByRef result As Decimal)
' Add up all the LineTotals on the Order_Details collection for this Order
result = Aggregate d In Me.Order_Details Into Sum(d.LineTotal)
End Sub
End Class
Next I want to send an automated email when the Order is inserted into the database. Open the Order entity in the designer and then drop down the “Write Code” button on the top right and select Order_Inserted to generate the method stub. To generate HTML all you need to do is type well formed XHTML into the editor and use embedded expressions to pull the data out of the entities.
Public Class NorthwindDataService
Private Sub Orders_Inserted(ByVal entity As Order)
Dim toEmail = entity.Customer.Email
If toEmail <> "" Then
Dim fromEmail = entity.Employee.Email
Dim subject = "Thank you for your order!"
Dim body = <html>
<body style="font-family: Arial, Helvetica, sans-serif;">
<p><%= entity.Customer.ContactName %>, thank you for your order!<br></br>
Order date: <%= FormatDateTime(entity.OrderDate, DateFormat.LongDate) %></p>
<table border="1" cellpadding="3"
style="font-family: Arial, Helvetica, sans-serif;">
<tr>
<td><b>Product</b></td>
<td><b>Quantity</b></td>
<td><b>Price</b></td>
<td><b>Discount</b></td>
<td><b>Line Total</b></td>
</tr>
<%= From d In entity.Order_Details
Select <tr>
<td><%= d.Product.ProductName %></td>
<td align="right"><%= d.Quantity %></td>
<td align="right"><%= FormatCurrency(d.UnitPrice, 2) %></td>
<td align="right"><%= FormatPercent(d.Discount, 0) %></td>
<td align="right"><%= FormatCurrency(d.LineTotal, 2) %></td>
</tr>
%>
<tr>
<td></td>
<td></td>
<td></td>
<td align="right"><b>Total:</b></td>
<td align="right"><b><%= FormatCurrency(entity.OrderTotal, 2) %></b></td>
</tr>
</table>
</body>
</html>
SMTPMailHelper.SendMail(fromEmail, toEmail, subject, body.ToString)
End If
End Sub
End Class
The trick is to make sure your HTML looks like XML (i.e. well formed begin/end tags) and then you can use embedded expressions (the <%= syntax) to embed Visual Basic code into the HTML. I’m using LINQ to query the order details to populate the rows of the HTML table. (BTW, you can also query HTML with a couple tricks as I show here).
So now when a new order is entered into the system an auto-generated HTML email is sent to the customer with the order details.
Sending Email via an Outlook Client
The above solution works well for sending automated emails but what if you want to allow the user to modify the email before it is sent? In this case we need a solution that can be called from the LightSwitch UI. One option is to automate Microsoft Outlook -- most people seem to use that popular email client, especially my company ;-). Out of the box, LightSwitch has a really nice feature on data grids that lets you to export them to Excel if running in full trust. We can add a similar Office productivity feature to our screen that auto generates an email for the user using Outlook. This will allow them to modify it before it is sent.
We need a helper class on the client this time. Just like in the SMTP example above, add a new class via the Solution Explorer file view but this time select the Client project. This class uses COM automation, a feature of Silverlight 4 and higher. First we need to check if we’re running out-of-browser on a Windows machine by checking the AutomationFactory.IsAvailable property. Next we need to get a reference to Outlook, opening the application if it’s not already open. The rest of the code just creates the email and displays it to the user.
Imports System.Runtime.InteropServices.Automation
Public Class OutlookMailHelper
Const olMailItem As Integer = 0
Const olFormatPlain As Integer = 1
Const olFormatHTML As Integer = 2
Public Shared Sub CreateOutlookEmail(ByVal toAddress As String,
ByVal subject As String,
ByVal body As String)
Try
Dim outlook As Object = Nothing
If AutomationFactory.IsAvailable Then
Try
'Get the reference to the open Outlook App
outlook = AutomationFactory.GetObject("Outlook.Application")
Catch ex As Exception 'If Outlook isn't open, then an error will be thrown.
' Try to open the application
outlook = AutomationFactory.CreateObject("Outlook.Application")
End Try
If outlook IsNot Nothing Then
'Create the email
' Outlook object model (OM) reference:
' https://msdn.microsoft.com/en-us/library/ff870566.aspx
Dim mail = outlook.CreateItem(olMailItem)
With mail
If body.ToLower.Contains("<html>") Then
.BodyFormat = olFormatHTML
.HTMLBody = body
Else
.BodyFormat = olFormatPlain
.Body = body
End If
.Recipients.Add(toAddress)
.Subject = subject
.Save()
.Display()
'.Send()
End With
End If
End If
Catch ex As Exception
Throw New InvalidOperationException("Failed to create email.", ex)
End Try
End Sub
End Class
The code to call this is almost identical to the previous example. We use XML literals to create the HTML the same way. The only difference is we want to call this from a command button on our OrderDetail screen. (Here’s how you add a command button to a screen.) In the Execute method for the command button is where we add the code to generate the HTML email. I also want to have the button disabled if AutomationFactory.IsAvailable is False and you check that in the CanExecute method.
Here’s the code we need in the screen:
Private Sub CreateEmail_CanExecute(ByRef result As Boolean)
result = System.Runtime.InteropServices.Automation.AutomationFactory.IsAvailable
End Sub
Private Sub CreateEmail_Execute()
'Create the html email from the Order data on this screen
Dim toAddress = Me.Order.Customer.Email
If toAddress <> "" Then
Dim entity = Me.Order
Dim subject = "Thank you for your order!"
Dim body = <html>
<body style="font-family: Arial, Helvetica, sans-serif;">
<p><%= entity.Customer.ContactName %>, thank you for your order!<br></br>
Order date: <%= FormatDateTime(entity.OrderDate, DateFormat.LongDate) %></p>
<table border="1" cellpadding="3"
style="font-family: Arial, Helvetica, sans-serif;">
<tr>
<td><b>Product</b></td>
<td><b>Quantity</b></td>
<td><b>Price</b></td>
<td><b>Discount</b></td>
<td><b>Line Total</b></td>
</tr>
<%= From d In entity.Order_Details
Select <tr>
<td><%= d.Product.ProductName %></td>
<td align="right"><%= d.Quantity %></td>
<td align="right"><%= FormatCurrency(d.UnitPrice, 2) %></td>
<td align="right"><%= FormatPercent(d.Discount, 0) %></td>
<td align="right"><%= FormatCurrency(d.LineTotal, 2) %></td>
</tr>
%>
<tr>
<td></td>
<td></td>
<td></td>
<td align="right"><b>Total:</b></td>
<td align="right"><b><%= FormatCurrency(entity.OrderTotal, 2) %></b></td>
</tr>
</table>
</body>
</html>
OutlookMailHelper.CreateOutlookEmail(toAddress, subject, body.ToString)
Else
Me.ShowMessageBox("This customer does not have an email address",
"Missing Email Address",
MessageBoxOption.Ok)
End If
End Sub
Now when the user clicks the Create Email button on the ribbon, the HTML email is created and the Outlook mail message window opens allowing the user to make changes before they hit send.
I hope I’ve provided a couple options for sending HTML emails in your LightSwitch applications. Select the first option to use SMTP when you want automated emails sent from the server side. Select the second option to use the Outlook client when you want to interact with users that have Outlook installed and LightSwitch is running out-of-browser.
Enjoy!
Comments
Anonymous
January 27, 2011
Excellent post Beth..tnks! Your content is always educational and practical. How about an article about the various ways of incorporating LS data into Word docs (including tables) next! Content controls, Open/customxml, automation etc..which method is best?Anonymous
January 27, 2011
Thanks! I did write a post using Word to generate reports with user-defined templates. Check it out here: blogs.msdn.com/.../using-microsoft-word-to-create-reports-for-lightswitch-or-silverlight.aspx Cheers, -BAnonymous
January 28, 2011
Aweosme..tnks beth!Anonymous
February 02, 2011
My original thought on seeing this post: "Whats all this about blah, blah just show me the code blah, blah." I was wrong, you showed me a very simple way of doing what took much more code 2 years ago. I too now love xml literals, as of 10 minutes ago ! Bravo Beth.Anonymous
February 10, 2011
Hey Beth, I converted this to C#. However now im getting errors about DomainServices not exisiting in System.ServiceModel.DomainServices How can i correct this?Anonymous
February 11, 2011
The comment has been removedAnonymous
February 14, 2011
Would it be possible to popup the Outlook address book from code and have the selected item be returned to a field on a Lightswitch form?Anonymous
February 18, 2011
Hi Graeme, Yes you probably can using the COM object model, I'd have to dig in and play with it though as I don't know what methods to call off-hand. -BAnonymous
April 27, 2011
Quick question on your XML literals. If any value in my "Body" variable comes back as NULL, the entire "Body" becomes null. I've experienced this in SQL HTML email procedures too, but I can do a simple ISNULL(FieldName, '') to fix it. Can you tell me what VB/XML syntax I would use to handle this?Anonymous
June 07, 2011
Thanks for your example, Betty, but if I use an Exchange server?Anonymous
September 04, 2011
Hi Beth, I'm following your lightswitch blogs, videos, your samples everything is fine. But you are always using VB. :) Should you add C# codes of this blog "How To Send HTML Email from a LightSwitch Application" ? Please can you add C# codes for new videos, and blogs like this. Cheers,Anonymous
October 25, 2011
Hi Beth, Great article, thanks :D How do you add multiple mail recipients (Cc) when creating the email.Anonymous
April 15, 2012
Hi Beth, can you tell me how to send mails with attachments in LS?Anonymous
April 28, 2012
Hi Beth Your blog is awesome n very informative. plz provide C# code also with the blog post.Anonymous
July 19, 2012
i am unable to add microsoft.office.interop.outlook reference in class . i am using c# coding . will you help me in this thing ?Anonymous
September 17, 2012
Thank you so much for you videos and blogs on LightSwitch. As a small business IT Manager is about to embark on writing a new in-house quoting system and inventory system, I'm heavily indebted to you for your great tutorials!Anonymous
October 15, 2012
In C#: using System; using System.Linq; using System.IO; using System.IO.IsolatedStorage; using System.Collections.Generic; using Microsoft.LightSwitch; using Microsoft.LightSwitch.Framework.Client; using Microsoft.LightSwitch.Presentation; using Microsoft.LightSwitch.Presentation.Extensions; using System.Runtime.InteropServices.Automation; using System.Text; using System.Windows; public static void CreateOutlookEmail(string toAddress, string subject, string body) { dynamic olMailItem = 0; dynamic olFormatPlain = 1; dynamic olFormatHTML = 2; try { dynamic outlook; if (AutomationFactory.IsAvailable) { try { // Get the reference to the open Outlook App outlook = AutomationFactory.GetObject("Outlook.Application"); } catch { // If Outlook isn't open, then an error will be thrown. // Try to open the application outlook = AutomationFactory.CreateObject("Outlook.Application"); } if (outlook != null) { // Create the email // Outlook object model (OM) reference: // msdn.microsoft.com/.../ff870566.aspx dynamic mail = outlook.CreateItem(olMailItem); // With... if (body.ToLower().Contains("<html>")) { mail.BodyFormat = olFormatHTML; mail.HTMLBody = body; } else { mail.BodyFormat = olFormatPlain; mail.Body = body; } mail.Recipients.Add(toAddress); mail.Subject = subject; mail.Save(); mail.Display(); // mail.Send() } } } catch (Exception ex) { throw new InvalidOperationException("Failed to create email.", ex); } }Anonymous
December 19, 2012
Hi Beth! i need help to make this HTML editable from client, it's possible? We are making a CRM tool, and there are some mails to we need to change any information every two months... i would like to make this on the frontside, not in code. thankS!!Anonymous
December 24, 2012
Thanks Michael Washington for providing me the mail code in c# language.Anonymous
December 24, 2012
Thanks Micheal! it really helped us resolve this issue!Anonymous
December 26, 2012
The comment has been removedAnonymous
January 10, 2013
I´m trying to send html email from a lightswitch application but when i try to get the image from the sql db it does not works. I used <img src="<%=me.opportunities.team.picture%>" width="80" height="80">. Any idea?Anonymous
November 30, 2013
Hello Everyone, Beth for what it's worth I'm using hotmail and I had to add : smtp.EnableSsl = True to the helper class and it worked like a charm after that. Thanks :)Anonymous
December 11, 2013
I am attempting to use the Send via Outlook. Ive added the codes, but nothing happens when i click the Button. I dont even get an error. Ive tried it with my Outlook running even and the same thing happens. Not sure how to troubleshoot when I dont even get an error.Anonymous
May 08, 2014
The comment has been removedAnonymous
July 09, 2014
Hello. is it possible to "Sending Email via an Outlook Client" with an Lightswitch HTML-Client?Anonymous
December 17, 2015
Beth, your blogs have been a lifesaver for my venture into Lightswitch! I am having an issue with an error that "SendMail is not a member of SMTPMailHelper" which makes no sense because I can see it clear as day in your code.