مطالب
تغییر اندازه تصاویر #2
در ادامه مطلب تغییر اندازه تصاویر #1 ، در این پست می‌خواهیم نحوه تغییر اندازه تصاویر را در زمان درخواست کاربر بررسی کنیم.

در پست قبلی بررسی کردیم که کاربر می‌تواند در دوحالت تصاویر دریافتی از کاربران سایت را تغییر اندازه دهد، یکی در زمان ذخیره سازی تصاویر بود و دیگری در زمانی که کاربر درخواست نمایش یک تصویر را دارد.

خوب ابتدا فرض می‌کنیم برای نمایش تصاویر چند حالت داریم مثلا کوچک، متوسط، بزرگ و حالت واقعی (اندازه اصلی).
البته دقت نمایید که این طبقه بندی فرضی بوده و ممکن است برای پروژه‌های مختلف این طبقه بندی متفاوت باشد. (در این پست قصد فقط اشنایی با تغییر اندازه تصاویر است و شاید کد به درستی refactor نشده باشد).
برای تغییر اندازه تصاویر در زمان اجرا یکی از روش ها، می‌تواند استفاده از Handler باشد. خوب برای ایجاد Handler ابتدا در پروژه وب خود بروی پروژه راست کلیک کرده، و گزینه New Item را برگزینید، و در پنجره ظاهر شده مانند تصویر زیر گزینه Generic Handler  را انتخاب نمایید.

پس از ایجاد هندلر، فایل کد آن مانند زیر خواهد بود، ما باید کدهای خود را در متد ProcessRequestبنویسیم.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace PWS.UI.Handler
{
    /// <summary>
    /// Summary description for PhotoHandler
    /// </summary>
    public class PhotoHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            context.Response.Write("Hello World");
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

خوب برای نوشتن کد در این مرحله ما باید چند کار انجام دهیم.
1- گرفتن پارامتر‌های ورودی کاربر جهت تغییر سایز از طریق روش‌های انتقال مقادیر بین صفحات (در اینجا استفاده از Query String ).
2-بازیابی تصویر از دیتابیس یا از دیسک به صورت یک آرایه بایتی.
3- تغییر اندازه تصویر مرحله 2 و ارسال تصویر به خروجی.
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Web;

namespace PWS.UI.Handler
{
    /// <summary>
    /// Summary description for PhotoHandler
    /// </summary>
    public class PhotoHandler : IHttpHandler
    {

        /// <summary>
        /// بازیابی تصویر اصلی از بانک اطلاعاتی
        /// </summary>
        /// <param name="photoId">کد تصویر</param>
        /// <returns></returns>
        private byte[] GetImageFromDatabase(int photoId)
        {
            using (var connection = new SqlConnection("ConnectionString"))
            {
                using (var command = new SqlCommand("Select Photo From tblPhotos Where Id = @PhotoId", connection))
                {
                    command.Parameters.Add(new SqlParameter("@PhotoId", photoId));
                    connection.Open();
                    var result = command.ExecuteScalar();
                    return ((byte[])result);
                }
            }
        }

        /// <summary>
        /// بازیابی فایل از دیسک
        /// </summary>
        /// <param name="photoId">با فرض اینکه نام فایل این است</param>
        /// <returns></returns>
        private byte[] GetImageFromDisk(string photoId /* or somting */)
        {
                using (var sourceStream = new FileStream("Original File Path + id", FileMode.Open, FileAccess.Read))
                {
                    return StreamToByteArray(sourceStream);
                }
        }

        /// <summary>
        /// Streams to byte array.
        /// </summary>
        /// <param name="inputStream">The input stream.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException"></exception>
        static byte[] StreamToByteArray(Stream inputStream)
        {
            if (!inputStream.CanRead)
            {
                throw new ArgumentException();
            }

            // This is optional
            if (inputStream.CanSeek)
            {
                inputStream.Seek(0, SeekOrigin.Begin);
            }

            var output = new byte[inputStream.Length];
            int bytesRead = inputStream.Read(output, 0, output.Length);
            Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length");
            return output;
        }

        /// <summary>
        /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
        public void ProcessRequest(HttpContext context)
        {
            // Set up the response settings
            context.Response.ContentType = "image/jpeg";
            context.Response.Cache.SetCacheability(HttpCacheability.Public);
            context.Response.BufferOutput = false;

            // مرحله اول
            int size = 0;
            switch (context.Request.QueryString["Size"])
            {
                case "S":
                    size = 100; //100px
                    break;
                case "M":
                    size = 198; //198px
                    break;
                case "L":
                    size = 500; //500px
                    break;
            }
            byte[] changedImage;
            var id = Convert.ToInt32(context.Request.QueryString["PhotoId"]);
            byte[] sourceImage = GetImageFromDatabase(id);
            // یا
            //byte[] sourceImage = GetImageFromDisk(id.ToString(CultureInfo.InvariantCulture));

            //مرحله 2
            if (size != 0)  //غیر از حالت واقعی تصویر
            {
                changedImage = Helpers.ResizeImageFile(sourceImage, size, ImageFormat.Jpeg);
            }
            else
            {
                changedImage = (byte[])sourceImage.Clone();
            }

            // مرحله 3
            if (changedImage == null) return;
            context.Response.AddHeader("Content-Length", changedImage.Length.ToString(CultureInfo.InvariantCulture));
            context.Response.BinaryWrite(changedImage);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}
در این هندلر ما چند متد اضافه کردیم.
1- متد GetImageFromDatabase: این متد یک کد تصویر را گرفته و آن را از بانک اطلاعاتی بازیابی میکند. (در صورتی که تصویر در بانک ذخیره شده باشد)
2- متد GetImageFromDisk: این متد نام تصویر (با فرض اینکه یک عدد می‌باشد) را به عنوان پارامتر گرفته و آنرا بازیابی می‌کند (در صورتی که تصویر در دیسک ذخیره شده باشد.)
3- متد StreamToByteArray: زمانی که تصویر از فایل خوانده می‌شود به صورت Stream است این متد یک Stream را گرفته و تبدیل به یک آرایه بایتی می‌کند.

در نهایت در متد ProcessRequestتصویر خوانده شده با توجه به پارامترهای ورودی تغییر اندازه داده شده و در نهایت به خروجی نوشته می‌شود.

برای استفاده این هندلر، کافی است در توصیر خود به عنوان مسیر رشته ای شبیه زیر وارد نمایید:
PhotoHandler.ashx?PhotoId=10&Size=S

مانند

<img src='PhotoHandler.ashx?PhotoId=10&Size=S' alt='تصویر ازمایشی' />
پ.ن : هرچند می‌توانستیم کد هارا بهبود داده و خیلی بهینه‌تر بنویسیم اما هدف فقط اشنایی با عمل تغییر اندازه تصویر در زمان اجرا بود، امیدوارم اساتید من ببخشن.

نظرات اقای موسوی تا حدودی اعمال شد و در پست تغییراتی انجام شد.
موفق وموید باشید

اشتراک‌ها
کتابخانه ساده جاوااسکریپتی برای کارهای بومی زبان فارسی

کتابخانه‎‌ای جاوااسکریپتی برای تبدیل اعداد انگلیسی به فارسی و بر عکس. ی و ک عربی و فارسی و اعداد عربی و فارسی

اما ویژگی جالب این کتابخانه در Decode کردن آدرس لینک‎های دارای کلمات فارسی و همچنین تبدیل حروف انگلیسی به فارسی معادل صفحه کلید. مانند تصویر زیر

کتابخانه ساده جاوااسکریپتی برای کارهای بومی زبان فارسی
نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت دوم
با سلام مجدد- ممنون از راهنمایی خیلی خوبتون. با استفاده از elmah تونستم جزئیات خطا رو ثبت کنم اما چیزی ازش متوجه نشدم. فقط انقد فهمیدم که آدرس عکس و به عنوان Action میشناسه! و چون وجود خارجی نداره خطای ControllerFactory میده. نمیدونم چرا!؟ 
نظرات مطالب
ساخت یک گزارش ساز به کمک iTextSharp و Open Office
- در مورد اجرا نشدن برنامه نصاب نظری ندارم. عموما این فایل‌ها دارای یک امضای دیجیتال md5 یا sha1 منتشر شده در سایت اصلی هم هستند. مقایسه کنید آیا کامل دریافت شده یا نه.
- در مورد تصویر، می‌تونید از روش‌های متداول iTextSharp استفاده کنید. PDF در اصل یک قالب برداری است. شما یک Canvas دارید که می‌تونید در هر جایی از آن هر شیءایی را قرار دهید. برای نمونه در مثال فوق:
PdfContentByte content = stamper.GetOverContent(pdfReader.NumberOfPages);
Image image = Image.GetInstance(imagePath);
image.SetAbsolutePosition(450,650);
image.ScaleAbsolute(200,200);
content.AddImage(image);
شما به کمک stamper دسترسی به این Canvas پیدا می‌کنید. سپس در هر مختصات دلخواهی مطابق کدهای فوق، تصویر مورد نظر را قرار دهید.
مطالب
تغییرات متدهای بازگشت فایل‌ها به سمت کلاینت در ASP.NET Core
اگر خروجی return File را در اکشن متدهای ASP.NET Core همانند ASP.NET MVC 5.x مورد استفاده قرار دهید و در آن مسیرکامل فایل را برای بازگشت قید کرده باشید، پیام یافت نشدن فایل را دریافت خواهید کرد؛ هرچند این فایل بر روی سرور و در مسیر ذکر شده وجود خارجی دارد. علت آن‌را در تصویر ذیل می‌توانید مشاهده کنید:



روش‌های مختلف بازگشت فایل‌ها به سمت کلاینت در ASP.NET Core

در ASP.NET Core، نوع‌های کاملتری از Action Result‌های مرتبط با بازگشت فایل‌ها تدارک دیده شده‌اند که نحوه‌ی طراحی آن‌ها را در شکل فوق ملاحظه می‌کنید. در اینجا FileResult والد تمام حالت‌های بازگشت فایل است که شامل موارد ذیل می‌شود:
FileContentResult: از آن برای بازگشت آرایه‌ای از بایت‌ها استفاده می‌شود:
//returns the file content as an array of bytes
public FileContentResult FileContentActionResult()
{
   var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   return File(file, "text/plain", "HomeController.cs");
}
زمانیکه Controller جاری از کلاس پایه Controller ارث بری می‌کند، متد File در این کلاس پایه قرار دارد. به همین جهت مانند مثال فوق به سادگی می‌توان به آن، بدون ذکر new دسترسی یافت. روش دیگر دسترسی به FileContentResult به صورت ذیل است که معادل قطعه کد فوق می‌باشد:
public IActionResult TestFileContentActionResult()
{
   var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   return new FileContentResult(file, "text/plain") { FileDownloadName = "HomeController.cs" };
}

FileStreamResult: این Action Result قابلیت Streaming بازگشت فایل‌ها را مهیا می‌کند:
//return the file as a stream
public FileStreamResult FileStreamActionResult()
{
   //var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   //var stream = new MemoryStream(file, writable:true);

   var fileStream = new FileStream(@"C:\path\dir1\HomeController.cs", FileMode.Open, FileAccess.Read);
   return File(fileStream, "text/plain", "HomeController.cs");
}
در اینجا برای مثال می‌توان یک MemoryStream و یا یک FileStream را به سمت کاربر ارسال کرد. این روش نسبت به خواندن فایل‌ها در آرایه‌ای از بایت‌ها و سپس ارسال یکجای آن، بهینه‌تر است و حافظه‌ی کمتری را مصرف می‌کند.
اگر خواستیم مستقیما با FileStreamResult کار کنیم، روش کار به صورت ذیل است:
public IActionResult TestFileStreamActionResult()
{
   //var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   //var stream = new MemoryStream(file, writable:true);
   var fileStream = new FileStream(@"C:\path\dir1\HomeController.cs", FileMode.Open, FileAccess.Read);
   return new FileStreamResult(fileStream, "text/plain") { FileDownloadName = "HomeController.cs" };
}

VirtualFileResult: در این مورد آدرسی را که ارائه می‌دهید، باید به فایلی درون پوشه‌ی wwwroot اشاره کند (علت اصلی بروز مشکلی که در مقدمه‌ی بحث عنوان شد). در اینجا آدرس کامل فایل مدنظر نیست.
//returns a file specified with a virtual path
public VirtualFileResult VirtualFileActionResult()
{
   return File("/css/site.css", "text/plain", "site.css");
}
و یا معادل همین قطعه کد با استفاده از VirtualFileResult اصلی به صورت ذیل است:
public IActionResult TestVirtualFileActionResult()
{
   return new VirtualFileResult("/css/site.css", "text/plain") { FileDownloadName = "site.css" };
}

PhysicalFileResult: اگر قصد دارید آدرس کامل فایلی را مشخص کنید (بجای مسیر نسبی آن که از wwwroot شروع می‌شود؛ مانند حالت قبل)، اینبار باید از متد PhysicalFile استفاده کرد:
//returns the specified file on disk, that is it's physical address
public PhysicalFileResult PhysicalFileActionResult()
{
   return PhysicalFile(@"C:\path\dir1\HomeController.cs", "text/plain", "HomeController.cs");
}
این قطعه کد نیز بر اساس استفاده‌ی مستقیم از PhysicalFileResult شکل زیر را می‌تواند پیدا کند:
public IActionResult TestPhysicalFileActionResult()
{
   return new PhysicalFileResult(@"C:\path\dir1\HomeController.cs", "text/plain")
   {
      FileDownloadName = "HomeController.cs"
   };
}

در این متدها و کلاس‌ها، اگر FileDownloadName حاوی حروف اسکی نباشد، به صورت خودکار encoding از نوع RFC5987 بر روی آن اعمال خواهد شد.
نظرات مطالب
آپلود فایل‌ها توسط برنامه‌های React به یک سرور ASP.NET Core به همراه نمایش درصد پیشرفت
با سلام؛ در آپلود فایل در هنگام آپلود، فایل در ریشه پروژه و فولدر wwwroot آپلود میشود، تا اینجا مشکلی ندارم، ولی در هنگام خواندن فایل چون پروژه React در فولدر CientApp قرار دارد نمیتوانم آدرس فایل رو داشته باشم باید چیکار کنم واسه حل این مشکل، و سوال بعدی آیا باید کار خاصی بکنم واسه بعد از publish پروژه که بتونم فایل‌ها رو بخونم؟
مطالب
تغییرات بوجود آمده در Mobile Features-MVC4
یکی دیگه از امکاناتی که به MVC4 اضافه شده و برام جالب بود پشتیبانی توکار از مرورگرهای موبایل و تبلت‌ها است به این صورت که اگر به عنوان مثال یک فایل  Layout.cshtml  داشته باشیم و یک فایل   Layout.Mobile.cshtml  بسازیم MVC به صورت خودکار در زمانی که کاربر به وسیله موبایل یا تبلت به سایت ما وارد میشود تشخیص داده و  Layout  مربوط به موبایل را که  Layout.Mobile.cshtml   اعمال میکند.
در این رابطه کتابخانه  JQuery  افزونه بسیار قوی را ارائه داده که به راحتی میتوان از آن در برنامه خود استفاده کرد.
این افزونه فقط شامل چند فایل عکس, جاوا اسکریپ یا CSS نیست بلکه با پشتیانی کامل از صفحات لمسی ,تبلت‌ها , Smart Phone‌ها ویژگی قدرتمندی را به برنامه نویس میدهد.
در ادامه قصد دارم شما را با یک صفحه ساده ساخته شده توسط این کتابخانه قذرتمند آشنا کنم.
ابتدا یک پروژه خالی MVC4 ایجاد کنید.(هدف ما بیشتر برای آشنایی با کتابخانه JQuery Mobile است پس میتوان از یک صفحه Html ساده نیز استفاده نمود).
سپس در  کنسول Nuget برای نصب JQuery Mobile عبارت زیر را تایپ کنید.

PM> Install-Package jquery.mobile

حال پس از نصب آن شاهد اضافه شدن  فایلهای  عکس, جاوا اسکریپ و CSS هستید.
نکته ای که باید توجه کرد این است که اگر از MVC4  استفاده میکنید این فایلها چون در پوشه Content ودر Root این پوشه ایجاد میشود امکان دارد ظاهر اصلی سایت را بهم بزند و شاید هم بعضی از فایلهای جاوا اسکریپت شما اجرا نشود و این به علت ویژگی Bundling است که کل فایل هایی که در Root فولدر Content , Script قرار دارد را Bundle  میکند وامکان تداخل در فایلهای CSS و جاوااسکریپت وجود دراد.که میتوان فایلهای مربوط به JQuery Mobile  را در فولدر‌های جداگانه نگهداری کرد.(بازهم میگم ممکن است)
نکته دیگر این است که شما زمانی که به وسیله تبلت یا مویایل خود سایت را مشاهده میکنید ممکن است سایت را خیلی ریز ببینبد که با اضافه کردن یک متا تگ به شکل زیر قابل حل است.
 <meta name="viewport" content="width=device-width">
حال یک صفحه HTML خالی را باز کرده و کدهای زیر را وارد کنید:
<head>
    <meta name="viewport" content="width=device-width,initial-sclae=1" />
    <link href="Content/jquery.mobile-1.1.0.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script>
    <script src="Scripts/jquery.mobile-1.1.0.js" type="text/javascript"></script>

    <title></title>
</head>
<body>
    <div data-role="page">
        <div data-role="header" data-theme="b">
            <h1>this is a test
            </h1>
        </div>
        <div data-role="conent">
            <ul data-role="listview" data-filter="true" data-inset="true" data-theme="e">
                <li><a href="#">Water</a></li>
                <li><a href="#">Pepsi</a></li>
                <li><a href="#">Diet Pepsi</a></li>
                <li><a href="#">Beer</a></li>
                <a href="#" data-role="button" data-theme="b">Click ME</a>
            </ul>
        </div>
        <div data-role="footer" data-theme="b" data-position="fixed">
            <h1>footer
            </h1>
        </div>
    </div>
</body> 
توجه داشته باشید که ترتیب اضافه کردن script  ها به صفحه مهم است.
توضیح کد بالا:
 data-role="page"
مشخص کننده محدوده صفحه است. 
" data-role="header
مشخص کننده هدر صفحه است.
"data-theme="e
مشخص کننده تم  صفحه است.برای اطلاعات بیشتر در باره این تنظیم به این سایت JQueryMobile مراجع نمایید.
"data-role="listview
همانطور که از اسمش پیداست برای مشخص کردن listview است.وباقی کد نیز مشخص است.
 "data-filter="true
توسط ویژگی بالا یک فیلترینگ زیبا بر روی آیتم های listview خواهیم داشت.
" data-inset="true"
واگر مقدار true باشد لبه‌های  listview  به صورت گرد در خواهند آمد.

MVC Mobile app

در قسمت‌های بعدی توضیحات کاملتری ارائه خواهم داد.
در ضمن اگر قلت املاعی دارم به بزرگی خودتون ببخشید.;)

نظرات مطالب
استفاده از ادیتور CKEditor در صفحات ASP.NET
- ValidateRequest به صورت پیش فرض به هر نوع تگی عکس العمل نشون می‌ده؛ نه فقط به یک سری تگ خاص.
- در MVC می‌تونید فقط به ازای یک خاصیت، AllowHtml رو فعال کنید و نه به اجبار به ازای کل صفحه یا کل سایت (مثل وب فرم‌ها).
- بعد از اینکه AllowHtml رو فعال کردید، نیاز خواهید داشت ورودی کاربر را اندکی تمیز کنید.
مطالب
xamarin.android قسمت سوم
Theme
برای اینکه بتوانیم ظاهر گرافیکی layout‌ها را کنترل نماییم، از Theme که مجموعه‌ای از styleهای گرافیکی می‌باشد، استفاده می‌کنیم. در اندروید مجموعه‌ای از تم‌های از پیش ساخته شده که به آنها Builtin Theme نیز گفته می‌شود می‌توانیم استفاده کنیم. تم‌ها ظاهر گرافیکی کلیه کنترل‌های Layout را با نام‌های زیر، کنترل می‌کنند:
statusBarColor
textColorPrimary
colorAccent
ColorPrimary
WindowBackground

اگر ساختار زیر را در یک صفحه استاندارد برنامه‌های موبایل را در نظر بگیریم، styleها هر بخش، یک نام منحصر به فرد دارد:



اگر بخواهیم از style‌های از پیش طراحی شده‌ی اندروید استفاده نماییم، ابتدا میتوانیم در صفحه‌ی layout در زامارین، style مربوطه را از بخش Theme استفاده کرده و نتیجه را مشاهده کنیم. ولی تغییر style سبب تغییر layout در زمان اجرا نمی‌شود. هرگاه بخواهیم از styleهای استاندارد یا builtin اندروید استفاده نماییم، در Activity توسط خصوصیت Theme با فرمت:
[Activity(Theme = "@style/NameThem")]
تم را به‌عنوان تم داخلی وسپس نام کامل تم را می‌نویسیم.
 
CustomTheme
در طراحی فرم‌ها ممکن است بخواهیم از یک استایل خاص builtin استفاده کنیم؛ ولی ممکن است بعضی از استایل‌های آن را استفاده نکنیم، مانند تمی که از قبل استفاده شده‌است، از روش زیر استفاده می‌کنیم:
- بر روی دایرکتوری value راست کلیک میکنیم. گزینه add new item را انتخاب و یک فایل xmlfile را با نام style ایجاد میکنیم.
- style‌های جدید منابع application می‌باشند که در بخش resource از آن‌ها استفاده میکنیم. هر استایل جدید را توسط Style Tag مشخص میکنیم و در خصوصیت Name، نام Style را مشخص میکنیم.
ممکن است در یک Style نتوانیم و یا نخواهیم تمامی Style‌های مورد نیاز را تامین کنیم. از این رو توسط Parent، یک StyleBuition تعریف نموده که این Styleها از آن مشتق می‌شوند. اگر در Theme جدید گزینه‌ها مشخص شوند، تم اصلی تغییر نمی‌کند. در غیر اینصورت تمامی گزینه‌های تعریف شده در تم جدید از ThemParent مقدار دهی می‌شود.
توسط item می‌توان یک style را تعریف نمود. در Activity توسط
 [Activity(Theme = "@style/NameThem")]
می‌توانیم استایل سفارشی شده خودمان را مشخص کنیم.

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <style name="MainTheme" parent="Theme.AppCompat.Light.DarkActionBar">
     <item name="windowNoTitle">true</item>
     <item name="windowActionBar">false</item>
  </style>
</resources>
 
  
Navigation Menu
کتابخانه متریال دیزاین کتابخانه جدیدی می‌باشد که به اندروید اضافه شده‌است. توسط  آن می‌توانیم کنترل‌های جدید را با استایل‌های جدید برای Appهای اندروید، تولید کنیم. ابتدا نیاز به نصب Component‌های ذیل در زامارین می‌باشد.
 
استفاده که از کتابخانه متریال دیزاین
برای اینکه بتوانیم Navigation menu را ایجاد کنیم، باید از نیوگت، کتابخانه‌های Appcompat و Designlibrary را انتخاب و نصب نماییم و اگر نگارش ویژوال استودیوی شما 15.7.3باشد، از ابتدا بصورت اتوماتیک نصب شده است و احتیاجی به این مراحل نمی‌باشد. بدیهی است در زمان نصب باید از نرم افزار‌های تحریم گذر نیز استفاده کرد.
  
ساخت Menu
NavigationMenu، منوی اصلی منو می‌باشد که با Swipping از گوشه راست به چپ، باز یا بسته می‌شود یا با کلیک بر روی دکمه‌ی Menu بر روی Toolbar، منو را باز یا بسته میکند.
قدم اول: نصب منو
منظور همان اضافه کردن کتابخانه‌ها است.
قدم دوم:
ابتدا باید گزینه‌های منو را در یک فایل xml تعریف نمود. هر گزینه‌ی آن از دو بخش متن اصلی منو و ID منو تشکیل شده‌است.
بر روی دایرکتوری Resource راست کلیک کرده و یک دایرکتوری یا پوشه را به نام Menu ایجاد می‌کنیم. بر روی دایرکتوری منو، راست کلیک کرده و یک فایل Xml را به آن اضافه می‌کنیم. برای آنکه بتوانیم در این فایل دستورات ساخت منو را نوشته و به نحوی که توسط اندروید قابل خواندن و تبدیل به منو باشد، ساختار منو را از آدرس زیر ویژوال استودیو دانلود میکنیم.
 <menu xmlns:android="http://schemas.android.com/apk/res/android">

توسط Group مجموعه گزینه‌های منو را را معرفی میکنیم.
هر گزینه در منو ی اصلی توسط یک آیتم مشخص میشود. برای آنکه هنگام کلیک بر روی هر گزینه از طریق برنامه نویسی بتوانیم گزینه انتخاب شده را شناسایی کنیم، یک آی دی منحصر بفرد را به هر گزینه اختصاص میدهیم. زمانیکه بر روی یک گزینه کلیک میشود، توسط این ID‌ها میتوانیم شناسایی کنیم کدام گزینه انتخاب شده‌است.
خصوصیت Android :Title متن اصلی منو را مشخص میکند.
 <?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <group android:checkableBehavior="single">
     <item android:id="@+id/menuItemHome" android:title="صفحه اصلی"></item>
     <item android:id="@+id/menuItemInsertProduct" android:title="ورود کالا جدید" ></item>
     <item android:id="@+id/menuItemListProduct" android:title="مشاهده کالاها"></item>
     <item android:id="@+id/menuItemExit"  android:title="خروج"></item>
  </group>
</menu>

سپس باید در Layout مورد نظر همانند صفحه Main، ساختار اصلی برنامه شامل Toolbar و Menu را بصورت زیر تعریف نماییم:
<android.support.v7.widget.Toolbar
  android:layout_width="match_parent"
  android:id="@+id/toolbar1"
  android:background="#33B86C"
  android:minHeight="?android:attr/actionBarSize"
  android:layout_height="wrap_content">
</android.support.v7.widget.Toolbar>

ساختار منو به صورت زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <group android:checkableBehavior="single">
    <item android:id="@+id/menuItemHome" android:title="صفحه اصلی"></item>
    <item android:id="@+id/menuItemInsertProduct" android:title="ورود کالا جدید" ></item>
    <item android:id="@+id/menuItemListProduct" android:title="مشاهده کالاها"></item>
    <item android:id="@+id/menuItemExit"  android:title="خروج"></item>
  </group>
</menu>
در Linearlayout ریشه، گزینه Fitssystemwindow را true میکنیم که سایز linearlayout را با سایز موبایل جدید اندازه می‌کند. سپس از toolbox، کنترل Toolbarرا به پنجره اضافه میکنیم که در بالای صفحه قرار می‌گیرد.
toolbar اضافه شده، toolbar استاندارد قبل از متریال دیزاین میباشد. در واقع toolbar اول، Toolbar استاندارد اندروید می‌باشد. برای آنکه از Toolbar متریال دیزاین استفاده کنیم، کنترل‌های متریال دیزاین در بخش supportlibrary اضافه میشود و Toolbar متریال دیزاین را اضافه میکنیم. علامت ؟ یعنی اینکه می‌خواهیم از اندازه سیستمی استفاده کنیم. اگر بخواهیم حداقل سایز Toolbarبر اساس پیش فرض در دستگاه‌های مختلف باشد، از علامت Android :attr ? استفاده میکنیم. اگر بخواهیم حداقل ارتفاع پیشنهادی اندروید در هر موبایل متصل شود، از خصوصیت Action Bar Size  استفاده میکنیم. این خصوصیت زمانی عمل میکند که Height  آن Wrapcontent باشد.
گذاشتن دکمه منو: برای آنکه بتوانیم دکمه منو را به Toolbar اضافه کنیم، از دکمه Image Button استفاده میکنیم که یک دکمه‌ی معمولی می‌باشد ولی خلاصه‌ی آن عکس است. در خصوصیت Back ground دکمه، بصورت زیر نام فایل آیکن منو را در دایرکتوری Drawable، مشخص میکنیم و خصوصیت src آن‌را null می‌کنیم تا تصویری بجز تصویر انتخابی نباشد.
برای آنکه بتوانیم پنجره اصلی منو را به صورتیکه دارای قابلیت حرکت به راست و چپ باشد، ایجاد کنیم، از کنترلی به‌نام  Drowerlayout استفاده میکنیم که بر روی صفحه قرار میگیرد. DrawerLayout در linearlayout ریشه قرار میگیرد و یا بعد از ToolBar و حتما باید خصوصیت fitsystemwindow کنترل Drawer را True کنیم. جهت نمایش گزینه‌های اصلی در Drawer از کنترل NavigationٰView استفاده می‌کنیم.
گزینه‌های منو در کنترلی به نام Navigationview قرار دارد. این کنترل باید در Drawerlayout قرار گیرد. توسط فضای نام منو، محل فایل xml را که منو درون آن قرار گرفته است، مشخص می‌کنیم. آدرس این دستور در این مسیر می‌باشد:
 xmlns:app="http://schemas.android.com/apk/res-auto"
Layout gravity  آن را end  قرار میدهیم که از سمت راست قرار بگیرد. Fit system Window را هم True میکنیم تا گزینه‌های داخل آن‌را هم Fit کند. Theme باید از نوع تم‌های متریال دیزاین و با کلمه Them . App Compact. ligth.NoActionBar باشد. برای آنکه اکتیویتی‌ها، متریال دیزاین را ساپورت کنند، میتوان از کلاس  App compact Activity  استفاده کنیم. Tool bar بصورت پیش فرض لیبل اکتیویتی را نشان می‌دهد و دستور زیر عنوان Toolbar را حذف میکند.
 SupportActionBar.SetDisplayShowTitleEnabled(false);
 
مدیریت گزینه‌های منو
به محض انتخاب یک گزینه درون NavigationView، رخدادی به نام NavigationItemSelected صادر می‌شود که توسط آن میتوانیم گزینه‌ی انتخاب شده را از طریق برنامه نویسی مدیریت کنیم. این کنترل در Android.Support.V7.Widget و NameSpace بالا قرار میگیرد. سپس یک رخ‌داد گردان را با نام navigationItemSelected پیاده سازی می‌کنیم. اطلاعات مربوط به گزینه‌ی انتخاب شده، در پارامتر دوم از این تابع NavigationView.NavigationItemSelectedEventArgs ذخیره می‌شود. ID، آیتم انتخاب شده در فایل Menu را باز می‌گرداند.
        var navigationview = this.FindViewById<NavigationView>(Resource.Id.navigationView1);
        navigationview.NavigationItemSelected += Navigationview_NavigationItemSelected;
        private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
        {
            Intent intent = null;
            switch (e.MenuItem.ItemId)
            {
                case Resource.Id.menuItemHome:

                    break;
                case Resource.Id.menuItemExit:
                    Finish();
                    break;
                case Resource.Id.menuItemInsertProduct:

                    break;
                case Resource.Id.menuItemListProduct:

                    break;
            }
        }
 
مدیریت اکتیویتی‌ها توسط Menu
با انتخاب گزینه Menu باید اکتیویتی مربوطه انتخاب شود. بنابراین برای هر گزینه‌ی منو یک Layout و اکتیویتی را ایجاد می‌کنیم و اجرا میکنیم. ولی در اکتیویتی جدید Toolbar وجود ندارد.
 
تکنیک ادغام:
برای آنکه در Layoutهای مختلف، تولبار و منو و یا هر View دیگری را بصورت مشترک استفاده کنیم، یک فایل xml را به دایرکتوری Layout اضافه می‌کنیم. دستور Merge میتواند تمام layoutها را به درون layoutهای دیگر مانند home,insert ادغام و یا تزریق کند. جهت استفاده از Merge در layoutهای دیگر نیاز به Id منحصر به فرد می‌باشد.
<?xml version="1.0" encoding="utf-8" ?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/toolbarlayout">
    <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:id="@+id/toolbar1" android:background="#33B86C" android:minHeight="?android:attr/actionBarSize" android:layout_height="wrap_content">
        <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton1" android:background="@drawable/mainmenu" android:layout_gravity="end" />
    </android.support.v7.widget.Toolbar>
    <android.support.v4.widget.DrawerLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/drawerLayout1" android:fitsSystemWindows="true">
        <android.support.design.widget.NavigationView android:minWidth="25px" android:minHeight="25px" android:layout_width="200dp" android:layout_height="match_parent" android:layout_gravity="end" app:menu="@menu/menu" android:id="@+id/navigationView1" android:fitsSystemWindows="true" />
    </android.support.v4.widget.DrawerLayout>
</merge>

در اکتیویتی‌های دیگر باید Toolbar و مدیریت گزینه‌های منو با کد‌های مشابه Main انجام شود.
        private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
        {
            Intent intent = null;
            switch (e.MenuItem.ItemId)
            {
                case Resource.Id.menuItemHome:
                    intent = new Intent(this, typeof(MainActivity));
                    break;
                case Resource.Id.menuItemExit:
                    Finish();
                    break;
                case Resource.Id.menuItemInsertProduct:
                    intent = new Intent(this, typeof(InsertActivity));
                    break;
                case Resource.Id.menuItemListProduct:
                    intent = new Intent(this, typeof(ListProductsActivity));
                    break;
            }
            if (intent != null) { }
        }
بنابراین دستورات xmlTollbar  و darawer layout در تمامی Layoutها و دستورات سی شارپ، کنترل کننده Toolbar و منو در تمامی اکتیویتی‌ها تکرار شده‌اند.
 
حل مشکلات Layout
یک فایل Xml را به Layout  اضافه می‌کنیم و درون آن Tag merge و کد‌های مشترک Drawer out و Toolbar را داخل تگ Merge اضافه می‌کنیم. جهت استفاده از کدهای (مقدار فایل ایکس ام ال ساخته شده که Tag merge داخل آن است)  Merge، در layout های دیگر، از دستور Include  استفاده می‌کنیم.
نام لی‌آوت را در خصوصیت Layout اضافه می‌کنیم. برای آنکه کد‌های سی شارپ کنترل کننده‌ی Toolbar و Menu چندین Toolbar وجود دارد که در یکی از آن‌ها یک کلاس واسط از کلاس app compat Activity  را به ارث میبریم. تابع Protected را از آن بازنویسی کرده و تمام کد‌های مدیریت Toolbar و منو را در آن پیاده سازی می‌کنیم. تمام اکتیویتی‌های برنامه را از این کلاس به ارث می‌بریم. بنابراین تابع InitieToolbar به تمامی فرزندان نیز به ارث برده می‌شود. در زمان اجرای دستورات، this ، اکتیویتی جاری می‌باشد.
    public class BaseAcitivity : AppCompatActivity
    {
        protected void InitieToolbar()
        {
            var toolbar = this.FindViewById<widgetV7.Toolbar>(Resource.Id.toolbar1);
            this.SetSupportActionBar(toolbar);
            //SupportActionBar.SetDisplayShowTitleEnabled(false);
            var imagebutton = toolbar.FindViewById<ImageButton>(Resource.Id.imageButton1);
            imagebutton.Click += Imagebutton_Click;
            var navigationview = this.FindViewById<NavigationView>(Resource.Id.navigationView1);
            navigationview.NavigationItemSelected += Navigationview_NavigationItemSelected;
        }

        private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
        {
            Intent intent = null;
            switch (e.MenuItem.ItemId)
            {
                case Resource.Id.menuItemHome:
                    intent = new Intent(this, typeof(MainActivity));
                    break;
                case Resource.Id.menuItemExit:
                    Finish();
                    break;
                case Resource.Id.menuItemInsertProduct:
                    intent = new Intent(this, typeof(InsertActivity));
                    break;
                case Resource.Id.menuItemListProduct:
                    intent = new Intent(this, typeof(ListProductsActivity));
                    break;
            }
            if (intent != null)
                StartActivity(intent);
        }

        private void Imagebutton_Click(object sender, EventArgs e)
        {
            var drawerlayout = this.FindViewById<DrawerLayout>(Resource.Id.drawerLayout1);
            if (drawerlayout.IsDrawerOpen(Android.Support.V4.View.GravityCompat.End) == false)
            {
                drawerlayout.OpenDrawer(Android.Support.V4.View.GravityCompat.End);
            }
            else
            {
                drawerlayout.CloseDrawer(Android.Support.V4.View.GravityCompat.End);
            }
        }
    }
 
اگر بخواهیم یک تم در تمامی اکتیویتی‌ها  به صورت سراسری استفاده شود، از فایل تنظمیات اندروید بنام AndroidManifest در دایرکتوری Properties استفاده می‌کنیم و در  بخش Application Theme، نام تم را مشخص میکنیم:
 android:theme="@style/Theme.AppCompat.Light.NoActionBar"
 

ساخت TabPage
پیشنیاز: نصب کتابخانه‌های متریال دیزاین همانند قبل و طبق ورژن Sdk نصب شده
اگر بخواهیم چندین صفحه را بر روی یکدیگر Stack و یا Overload نماییم، از Tabpage استفاده می‌کنیم. صفحاتی‌که از TabPage استفاده می‌کنند، با انگشت جابجا میشوند و همانند برنامه‌ی واتساپ Fragment می‌باشند و هر Fragment دارای layout و اکتیویتی مربوط به خود می‌باشد. معماری layout آن بصورت زیر است:


ToolBar، در بالای فرم قرار می‌گیرد. TabLayou که بصورت TabPage آن‌ها را به عهده دارد. Viewpager مدیریت Layout‌ها را به هنگام Swipe یا جابجایی به عهده دارد.
یک layout را برای Toolbar قرار می‌دهیم. سپس Layout اصلی main را طراحی میکنیم. پس از اضافه کردن ToolBar، ابزار TabLayout را در بخش SupportLibrary متریال دیزاین انتخاب و در صفحه می‌کشیم. TabLayout در پایین Toolbar قرار می‌گیرد و با انتخاب رنگ یکسان برای هر دو، متصل و یکنواخت به نظر می‌رسد. سپس از Layout از Toolbar آیتم ViewPager را بر روی صفحه قرار می‌دهیم. اگر LayoutWeight آن را یک قرار دهیم، تمام ارتفاع صفحه را به ما تخصیص می‌دهد. زمانیکه در TabLayout تب‌ها جابجا می‌شوند یا بر روی یک آیکن کلیک می‌شود، صفحه مربوطه در بخش ViewPager به کاربر نمایش داده میشود. هر Page یک فرگمنت می‌باشد. به ازای هر فرگمنت یک Layout به دایرکتوری layout اضافه کرده و به ازای هر layoutFragment یک Activity Fragment را اضافه می‌کنیم. یک اکتیویتی از نوع را Android.Support.v4.AppFragment ایجاد میکنیم.
    public class Fragment1 : Android.Support.V4.App.Fragment
    {
        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
        }
        public override View OnCreateView(LayoutInflater inflater,
      ViewGroup container, Bundle savedInstanceState)
        {
            return inflater.Inflate(Resource.Layout.FragmentLayout1, container, false);
        }
    }
ابتدا باید viewpager در Layout اصلی را پیدا کرده و با دستور زیر به Tablayout متصل کنیم:
 var tablayout = FindViewById<Android.Support.Design.Widget.TabLayout>(Resource.Id.tabLayout1);
var viewpager = FindViewById<ViewPager>(Resource.Id.viewPager1);
tablayout.SetupWithViewPager(viewpager);
زمانیکه آیکن را در TabLayout انتخاب میکنیم یا با انگشت Swipe میکنیم، به ترتیب بین صفحات که از Position صفحه آغاز شده‌اند، حرکت می‌کنیم، باید فرگمنت همان Position را نشان دهیم و این مدیریت توسط بخشی به‌نام Adapter انجام میشود. یک Adapter را به کنترلر اضافه میکنیم و از کلاس Fragment pager adapter به ارث می‌بریم. بر روی کلاس Fragment pager adapter ، دکمه‌های کنترل و نقطه را میزنیم و سپس کلاس را پیاده سازی می‌کنیم. در این حالت دو تابع را به ما می‌دهد: تابع Get item .count مجددا بر روی کلاس پدر راست کلیک میکنیم. در تابع کانت تعداد کل صفحه‌ها را (Layout ها) را انتخاب میکنیم. هرگاه از یک صفحه به صفحه‌ی دیگری انتقال پیدا کنیم، موقعیت صفحه جدیدی که از یک شروع میشود را به تابع Get Item بر اساس موقعیت Object  از fragment مربوطه new کرده و بعنوان خروجی باز می‌گرداند.
    class TabFragmentAdapter : FragmentPagerAdapter
    {
        public TabFragmentAdapter(FragmentManager fm) : base(fm)
        {
        }
        public override int Count => 3;
        public override Fragment GetItem(int position)
        {
            switch (position)
            {
                case 0: return new Fragment1();
                case 1: return new Fragment2();
                case 2: return new Fragment3();
                default: return new Fragment1();
            }
        }


        //int f1() { return 100; }
        //int f1 => 100;
    }
و در اکتیویتی اصلی، کد زیر را برای Load فرگمنت‌ها نیز قرار می‌دهیم:
 viewpager.Adapter = new TabFragmentAdapter(this.SupportFragmentManager);
  
آیکن برای TabPage
سپس اگر بخواهیم آیکن‌های Tab را به ترتیب تعریف کنیم، از تابع Gettabat استفاده میکنیم. پارامتر ورودی آن موقعیت Tab page میباشد و Set icon هم آیکن‌های دایرکتوری Drawable را انتخاب میکند.
 tablayout.GetTabAt(0).SetIcon(Resource.Drawable.iconCall);
 

نمایش متن همراه با عکس
 اگر بخواهیم آیکن‌های تب پیج را سفارشی کنیم، از Layout استفاده میکنیم که عرض و ارتفاع آن wrap Content  باشند و درون آن یک Text view که معادل Lable میباشند، قرار میدهند:
 View iconlayout1 = LayoutInflater.Inflate(Resource.Layout.custom_TabIconLayout, null);
var txt = iconlayout1.FindViewById<TextView>(Resource.Id.tabTextIcon);
txt.Text = "تماس";
txt.SetCompoundDrawablesWithIntrinsicBounds(Resource.Drawable.iconCall, 0, 0, 0);
tablayout.GetTabAt(0).SetCustomView(iconlayout1);

کدهای مطلب جاری برای دریافت: Navigation-TabPage-samples.zip
مطالب
معرفی کتابخانه Postal برای ASP.NET MVC
Postal کتابخانه ای برای تولید و ارسال ایمیل توسط نما‌های ASP.NET MVC است. برای شروع این کتابخانه را به پروژه خود اضافه کنید. پنجره Package Manager Console  را باز کرده و فرمان زیر را اجرا کنید.
PM> Install-Package Postal

شروع به کار با Postal

نحوه استفاده از Postal در کنترلر‌های خود را در کد زیر مشاهده می‌کنید.
using Postal;

public class HomeController : Controller
{
  public ActionResult Index()
  {
      dynamic email = new Email("Example");
      email.To = "webninja@example.com";
      email.FunnyLink = DB.GetRandomLolcatLink();
      email.Send();
      return View();
  }
}
Postal نمای ایمیل را در مسیر Views\Emails\Example.cshtml جستجو می‌کند.
To: @ViewBag.To
From: lolcats@website.com
Subject: Important Message

Hello,
You wanted important web links right?
Check out this: @ViewBag.FunnyLink

<3


پیکربندی SMTP

Postal ایمیل‌ها را توسط SmtpClient ارسال می‌کند که در فریم ورک دات نت موجود است. تنظیمات SMTP را می‌توانید در فایل web.config خود پیکربندی کنید. برای اطلاعات بیشتر به MSDN Documentation مراجعه کنید.
<configuration>
  ...
  <system.net>
    <mailSettings>
      <smtp deliveryMethod="network">
        <network host="example.org" port="25" defaultCredentials="true"/>
      </smtp>
    </mailSettings>
  </system.net>
  ...
</configuration>

ایمیل‌های Strongly-typed

همه خوششان نمی‌آید از آبجکت‌های دینامیک استفاده کنند. علاوه بر آن آبجکت‌های دینامیک مشکلاتی هم دارند. مثلا قابلیت IntelliSense و یا Compile-time error را نخواهید داشت.
قدم اول - کلاسی تعریف کنید که از Email ارث بری می‌کند.
namespace App.Models
{
  public class ExampleEmail : Email
  {
    public string To { get; set; }
    public string Message { get; set; }
  }
}
قدم دوم - از این کلاس استفاده کنید!
public void Send()
{
  var email = new ExampleEmail
  {
    To = "hello@world.com",
    Message = "Strong typed message"
  };
  email.Send();
}
قدم سوم - نمایی ایجاد کنید که از مدل شما استفاده می‌کند. نام نما، بر اساس نام کلاس مدل انتخاب شده است. بنابراین مثلا ExampleEmail نمایی با نام Example.cshtml لازم دارد.
@model App.Models.ExampleEmail
To: @Model.To
From: postal@example.com
Subject: Example

Hello,
@Model.Message
Thanks!

آزمون‌های واحد (Unit Testing)

هنگام تست کردن کدهایی که با Postal کار می‌کنند، یکی از کارهایی که می‌خواهید انجام دهید حصول اطمینان از ارسال شدن ایمیل‌ها است. البته در بدنه تست‌ها نمی‌خواهیم هیچ ایمیلی ارسال شود.
Postal یک قرارداد بنام IEmailService و یک پیاده سازی پیش فرض از آن بنام EmailService ارائه می‌کند، که در واقع ایمیل‌ها را ارسال هم می‌کند. با در نظر گرفتن این پیش فرض که شما از یک IoC Container استفاده می‌کنید (مانند StructureMap, Ninject)، آن را طوری پیکربندی کنید تا یک نمونه از IEmailService به کنترلر‌ها تزریق کند. سپس از این سرویس برای ارسال آبجکت‌های ایمیل‌ها استفاده کنید (بجای فراخوانی متد ()Email.Send).
public class ExampleController : Controller 
{
    public ExampleController(IEmailService emailService)
    {
        this.emailService = emailService;
    }

    readonly IEmailService emailService;

    public ActionResult Index()
    {
        dynamic email = new Email("Example");
        // ...
        emailService.Send(email);
        return View();
    }
}
این کنترلر را با ساختن یک Mock از اینترفیس IEmailService تست کنید. یک مثال با استفاده از FakeItEasy را در زیر مشاهده می‌کنید.
[Test]
public void ItSendsEmail()
{
    var emailService = A.Fake<IEmailService>();
    var controller = new ExampleController(emailService);
    controller.Index();
    A.CallTo(() => emailService.Send(A<Email>._))
     .MustHaveHappened();
}

ایمیل‌های ساده و HTML

Postal ارسال ایمیل‌های ساده (plain text) و HTML را بسیار ساده می‌کند.
قدم اول - نمای اصلی را بسازید. این نما header‌ها را خواهد داشت و نما‌های مورد نیاز را هم رفرنس می‌کند. مسیر نما Views\Emails\Example.cshtml\~ است.
To: test@test.com
From: example@test.com
Subject: Fancy email
Views: Text, Html
قدوم دوم - نمای تکست را ایجاد کنید. به قوانین نامگذاری دقت کنید، Example.cshtml به Example.Text.cshtml تغییر یافته. مسیر فایل Views\Emails\Example.Text.cshtml است.
Content-Type: text/plain; charset=utf-8

Hello @ViewBag.PersonName,
This is a message
دقت داشته باشید که تنها یک Content-Type باید تعریف کنید.
قدم سوم - نمای HTML را ایجاد کنید (باز هم فقط با یک Content-Type). مسیر فایل Views\Emails\Example.Html.cshtml\~ است.
Content-Type: text/html; charset=utf-8

<html>
  <body>
    <p>Hello @ViewBag.PersonName,</p>
    <p>This is a message</p>
  </body>
</html>

ضمیمه ها

برای افزودن ضمائم خود به ایمیل ها، متد Attach را فراخوانی کنید.
dynamic email = new Email("Example");
email.Attach(new Attachment("c:\\attachment.txt"));
email.Send();


جاسازی تصاویر در ایمیل ها

Postal یک HTML Helper دارد که امکان جاسازی (embedding) تصاویر در ایمیل‌ها را فراهم می‌کند. دیگر نیازی نیست به یک URL خارجی اشاره کنید. 
ابتدا مطمئن شوید که فایل web.config شما فضای نام Postal را اضافه کرده است. این کار دسترسی به HTML Helper مذکور در نمای‌های ایمیل را ممکن می‌سازد.
<configuration>
  <system.web.webPages.razor>
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="Postal" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
</configuration>
متد EmbedImage تصویر مورد نظر را در ایمیل شما جاسازی می‌کند و توسط یک تگ </img> آن را رفرنس می‌کند.
To: john@example.org
From: app@example.org
Subject: Image

@Html.EmbedImage("~/content/postal.jpg")
Postal سعی می‌کند تا نام فایل تصویر را، بر اساس مسیر تقریبی ریشه اپلیکیشن شما تعیین کند.


Postal بیرون از ASP.NET

Postal می‌تواند نماهای ایمیل‌ها را بیرون از فضای ASP.NET رندر کند. مثلا در یک اپلیکیشن کنسول یا یک سرویس ویندوز.
این امر توسط یک View Engine سفارشی میسر می‌شود. تنها نماهای Razor پشتیبانی می‌شوند. نمونه کدی را در زیر مشاهده می‌کنید.
using Postal;

class Program
{
    static void Main(string[] args)
    {
        // Get the path to the directory containing views
        var viewsPath = Path.GetFullPath(@"..\..\Views");

        var engines = new ViewEngineCollection();
        engines.Add(new FileSystemRazorViewEngine(viewsPath));

        var service = new EmailService(engines);

        dynamic email = new Email("Test");
        // Will look for Test.cshtml or Test.vbhtml in Views directory.
        email.Message = "Hello, non-asp.net world!";
        service.Send(email);
    }
}

محدودیت ها: نمی توانید برای نمای ایمیل هایتان از Layout‌ها استفاده کنید. همچنین در نماهای خود تنها از مدل‌ها (Models) می‌توانید استفاده کنید، و نه ViewBag.


Email Headers:  برای در بر داشتن نام، در آدرس ایمیل از فرمت زیر استفاده کنید.

To: John Smith <john@example.org>
Multiple Values: برخی از header‌ها می‌توانند چند مقدار داشته باشند. مثلا Bcc و CC. اینگونه مقادیر را می‌توانید به دو روش در نمای خود تعریف کنید:
جدا کردن مقادیر با کاما:
Bcc: john@smith.com, harry@green.com
Subject: Example

etc
و یا تکرار header:
Bcc: john@smith.com
Bcc: harry@green.com
Subject: Example

etc

ساختن ایمیل بدون ارسال آن

لازم نیست برای ارسال ایمیل هایتان به Postal تکیه کنید. در عوض می‌توانید یک آبجکت از نوع System.Net.Mail.MailMessage تولید کنید و به هر نحوی که می‌خواهید آن را پردازش کنید. مثلا شاید بخواهید بجای ارسال ایمیل ها، آنها را به یک صف پیام مثل MSMQ انتقال دهید یا بعدا توسط سرویس دیگری ارسال شوند. این آبجکت MailMessage تمامی Header ها، محتوای اصلی ایمیل و ضمائم را در بر خواهد گرفت.
کلاس EmailService در Postal متدی با نام CreateMailMessage فراهم می‌کند.
public class ExampleController : Controller 
{
    public ExampleController(IEmailService emailService)
    {
        this.emailService = emailService;
    }

    readonly IEmailService emailService;

    public ActionResult Index()
    {
        dynamic email = new Email("Example");
        // ...

        var message = emailService.CreateMailMessage(email);
        CustomProcessMailMessage(message);        

        return View();
    }
}

در این پست با امکانات اصلی کتابخانه Postal آشنا شدید و دیدید که به سادگی می‌توانید ایمیل‌های Razor بسازید. برای اطلاعات بیشتر لطفا به سایت پروژه Postal  مراجعه کنید.