Send a GridView (all rows or selected rows) by e-mail in ASP.NET (C#)
"E-mail me this list" turns a GridView into the body of an HTML message. Old tutorials render the grid control itself into a string; building the table from the data is cleaner, survives e-mail clients' limited CSS, and makes it easy to send only the rows the user selected.
Build the HTML from the data
private static string ToHtmlTable(IEnumerable<OrderRow> rows)
{
var sb = new StringBuilder();
sb.Append("<table cellpadding=\"6\" cellspacing=\"0\" style=\"border-collapse:collapse;font-family:Arial,sans-serif;font-size:14px\">");
sb.Append("<tr style=\"background:#0e6a70;color:#ffffff\"><th align=\"left\">Order</th><th align=\"left\">Customer</th><th align=\"right\">Total</th></tr>");
foreach (var r in rows)
{
sb.Append("<tr style=\"border-bottom:1px solid #dddddd\">")
.Append("<td>").Append(r.OrderId).Append("</td>")
.Append("<td>").Append(HttpUtility.HtmlEncode(r.Customer)).Append("</td>")
.Append("<td align=\"right\">").Append(r.Total.ToString("N2", CultureInfo.GetCultureInfo("en-GB"))).Append("</td>")
.Append("</tr>");
}
sb.Append("</table>");
return sb.ToString();
}
E-mail clients ignore most stylesheets, so styles are inline and the table uses old-fashioned attributes such as cellpadding that every client understands. Text from the database is HTML-encoded.
All rows or only the ticked ones
<asp:GridView ID="gvOrders" runat="server" AutoGenerateColumns="false" DataKeyNames="OrderId">
<Columns>
<asp:TemplateField><ItemTemplate><asp:CheckBox ID="chkSend" runat="server" /></ItemTemplate></asp:TemplateField>
<asp:BoundField DataField="OrderId" HeaderText="Order" />
<asp:BoundField DataField="Customer" HeaderText="Customer" />
<asp:BoundField DataField="Total" HeaderText="Total" DataFormatString="{0:N2}" />
</Columns>
</asp:GridView>
<asp:TextBox ID="txtTo" runat="server" TextMode="Email" />
<asp:Button ID="btnSendAll" runat="server" Text="E-mail all" OnClick="btnSend_Click" CommandArgument="all" />
<asp:Button ID="btnSendSelected" runat="server" Text="E-mail selected" OnClick="btnSend_Click" CommandArgument="selected" />
protected void btnSend_Click(object sender, EventArgs e)
{
bool onlySelected = ((Button)sender).CommandArgument == "selected";
var ids = new HashSet<int>();
foreach (GridViewRow row in gvOrders.Rows)
{
if (!onlySelected || ((CheckBox)row.FindControl("chkSend")).Checked)
ids.Add((int)gvOrders.DataKeys[row.RowIndex].Value);
}
if (ids.Count == 0) { lblResult.Text = "No rows selected."; return; }
// Re-read from the database: never trust values shown in the page
List<OrderRow> rows = OrderRepository.GetByIds(ids);
using (var msg = new MailMessage())
using (var smtp = new SmtpClient()) // settings from <system.net><mailSettings>
{
msg.To.Add(new MailAddress(txtTo.Text.Trim()));
msg.Subject = $"Orders ({rows.Count})";
msg.IsBodyHtml = true;
msg.Body = "<p>Here are the requested orders:</p>" + ToHtmlTable(rows);
smtp.Send(msg);
}
lblResult.Text = "Sent.";
}
With paging, gvOrders.Rows only contains the current page; "all" then means "this page". To send everything, query the whole result instead of reading ids from the grid, and combine with keeping selections across pages.
Validate the recipient, or better, send only to the signed-in user's own address: a form that mails arbitrary content to arbitrary addresses is an open relay for spammers.
RenderControl, if you must
Rendering the GridView itself with gvOrders.RenderControl(htmlTextWriter) also works but requires overriding VerifyRenderingInServerForm, includes check boxes and links that make no sense in an e-mail, and depends on CSS classes the e-mail client will not have. The technique is shown in Export GridView data. SMTP configuration: Sending test mail through Gmail SMTP.
Official documentation
- Microsoft Learn: MailMessage, SmtpClient, HttpUtility.HtmlEncode