۱۳۸۷/۰۹/۱۸

تهيه قالب براي ايميل‌هاي ارسالي يك برنامه ASP.Net


فرض كنيد ايميل اطلاع رساني برنامه ASP.Net شما قرار است ايميل زير را پس از تكميل يك فرم ارسال كند.


براي ارسال اين قالب كه مطابق تصوير هر بار بايد سه برچسب آن تغيير كند چه راهي را پيشنهاد مي‌دهيد؟

راه اول: (راه متداول)
اين فرم را در يك html editor درست كرده و جاي سه برچسب را خالي مي‌گذاريم. سپس html مورد نظر را در تابع ارسال ايميل خود به صورت يك رشته تعريف نموده و جاهاي خالي را پر خواهيم كرد. مثلا:
            string Name = "علي";
string Desc = "منابع مورد نياز";
int Number = 10;
string content =
"<div dir=\"rtl\" style=\"text-align: right; font-family:Tahoma; font-size:9pt\">" +
"با سلام<br />" +
"احتراما آقاي/خانم" +
Name +
"&nbsp;درخواست چاپ" +
Desc +
"&nbsp;داراي" +
Number +
"&nbsp;صفحه را داده‌اند. لطفا جهت تائيد درخواست ايشان به برنامه مراجعه بفرمائيد.<br />" +
"<br />" +
"با تشكر</div>";

ايرادات:
  • الف) امكان مشاهده شكل نهايي تا زمانيكه ايميل مورد نظر را دريافت نكرده باشيم، وجود ندارد.
  • ب) اعمال تغييرات جديد به اين فرمت رشته‌اي مشكل است. هميشه استفاده از ابزارهاي بصري براي بهبود كار كمك بزرگي هستند كه در اين حالت از آن‌ها محروم خواهيم شد.
  • ج)اگر تغيير رسيده جديد، درخواست اضافه كردن ليست پرينت‌هاي قبلي اين شخص بود چه بايد كرد؟ آيا جدول مورد نظر را بايد به صورت دستي ايجاد و باز هم به صورت يك رشته به اين مجموعه اضافه كرد؟ در اين حالت از كنترل‌هاي استانداردي مانند GridView و امثال آن محروم خواهيم شد.
  • د) هر بار تغيير، نياز به recompile برنامه دارد.
بنابراين همانطور كه مشاهده مي‌كنيد، نگهداري اين روش مشكل است.

راه دوم: استفاده از قالب‌ها

خوشبختانه در ASP.Net امكان رندر كردن كنترل‌ها به صورت يك string‌ نيز موجود است. در مثال ما نياز است تا چندين كنترل در كنار هم قرار گيرند تا شكل نهايي را ايجاد كنند. بنابراين مي‌توان تمام آنها را در يك يوزر كنترل قرار داد. سپس بايد كل يوزر كنترل را به صورت يك رشته، رندر كرد كه در ادامه به آن خواهيم پرداخت.

اگر قالب فوق را بخواهيم در يك يوزر كنترل طراحي كنيم، سورس صفحه html يوزر كنترل به صورت زير خواهد بود (فايل WebUserControl1.ascx) :
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs"
Inherits="testWebForms87.WebUserControl1" %>
<div dir="rtl" style="text-align: right; font-family:Tahoma; font-size:9pt">
با سلام<br />
احتراما آقاي/خانم
<asp:Label ID="lblName" runat="server"></asp:Label>
&nbsp;درخواست چاپ
<asp:Label ID="lblDesc" runat="server"></asp:Label>
&nbsp;داراي
<asp:Label ID="lblNumber" runat="server"></asp:Label>
&nbsp;صفحه را داده‌اند. لطفا جهت تائيد درخواست ايشان به برنامه مراجعه بفرمائيد.<br />
<br />
با تشكر</div>

همچنين مي‌توان مقادير برچسب‌ها را از طريق خواصي كه براي يوزر كنترل تعريف خواهيم كرد، در تابع ارسال ايميل خود مقدار دهي نمائيم. در اين حالت در سورس صفحه يوزر كنترل داريم (فايل WebUserControl1.ascx.cs) :
        public string Name { get; set; }
public int Number { get; set; }
public string Desc { get; set; }

protected void Page_Load(object sender, EventArgs e)
{
lblNumber.Text = Number.ToString();
lblName.Text = Name;
lblDesc.Text = Desc;
}

تا اينجا طراحي اوليه محتواي ايميل به پايان مي‌رسد. ديگر از آن رشته كذايي خبري نيست و همچنين مي‌توان از designer ويژوال استوديو براي طراحي بصري قالب مورد نظر استفاده كرد كه اين مزيت بزرگي است و در آينده اگر نياز به تغيير متن ايميل ارسالي وجود داشت، تنها كافي است فايل ascx ما ويرايش شود (بدون نياز به كامپايل مجدد پروژه). يا در اينجا به سادگي براي مثال مي‌توان يك GridView را تعريف، طراحي و bind كرد.

مرحله بعد، رندر كردن خودكار اين يوزر كنترل و سپس تبديل محتواي حاصل به يك رشته است. براي اين منظور از تابع زير مي‌توان كمك گرفت (براي مثال تعريف شده در كلاس دلخواه CLoadUC) :
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.UI;

/// <summary>
/// تبديل يك يوزر كنترل به معادل اچ تي ام ال آن
/// </summary>
/// <param name="path">مسير يوزر كنترل</param>
/// <param name="properties">ليست خواص به همراه مقادير مورد نظر</param>
/// <returns></returns>
/// <exception cref="NotImplementedException"><c>NotImplementedException</c>.</exception>
public static string RenderUserControl(string path,
List<KeyValuePair<string,object>> properties)
{
Page pageHolder = new Page();

UserControl viewControl =
(UserControl)pageHolder.LoadControl(path);

Type viewControlType = viewControl.GetType();

foreach (var pair in properties)
{
PropertyInfo property =
viewControlType.GetProperty(pair.Key);

if (property != null)
{
property.SetValue(viewControl, pair.Value, null);
}
else
{
throw new NotImplementedException(string.Format(
"UserControl: {0} does not have a public {1} property.",
path, pair.Key));
}
}

pageHolder.Controls.Add(viewControl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}

ماخذ اصلي تابع فوق اين آدرس است.
كه البته تابع نهايي آنرا كمي اصلاح كردم تا بتوان ليستي از خواص پابليك يك يوزر كنترل را به آن پاس كرد و محدود به يك خاصيت نبود.
اكنون استفاده از يوزر كنترلي كه تاكنون طراحي كرده‌ايم به سادگي زير است:
            List<KeyValuePair<string, object>> lst =
new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("Name", "علي"),
new KeyValuePair<string, object>("Number", 10),
new KeyValuePair<string, object>("Desc", "منابع مورد نياز")
};

string content = CLoadUC.RenderUserControl("WebUserControl1.ascx", lst);

ابتدا ليستي از خواص پابليك يوزر كنترل تهيه شده و مقدار دهي مي‌شوند . سپس مسير فايل يوزر كنترل به همراه اين ليست به تابع رندر كردن نهايي پاس خواهند شد. حاصل، رشته‌ html محتواي ايميل ما را تشكيل خواهد داد.