مطالب
قرار دادن نمودارهای MS Chart در گزارشات PdfReport
در حالت کلی، هر شیءایی را که بتوان تبدیل به تصویر کرد، قابلیت قرارگیری در یک فایل PDF را هم خواهد داشت. از این نمونه می‌توان به اشیاء MSChart اشاره کرد که از دات نت 4 جزئی از کتابخانه‌های اصلی دات نت شده‌اند و البته برای دات نت سه و نیم نیز به صورت جداگانه قابل دریافت است.
در ادامه مثالی را بررسی خواهیم کرد که بر اساس ردیف‌های گزارش آن، یک نمودار، به انتهای گزارش اضافه خواهد شد.
کدهای کامل این مثال را در ذیل مشاهده می‌کنید:
using System;
using System.Collections.Generic;
using PdfReportSamples.Models;
using PdfRpt.Core.Contracts;
using PdfRpt.Core.Helper;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.ChartImage
{
    public class ChartImagePdfReport
    {
        public IPdfReportData CreatePdfReport()
        {
            var chart = new MSChartHelper
                {
                    AxisXTitle = "User",
                    AxisYTitle = "Balance",
                    ChartTitle = "Users Balance",
                    AxisTitleFont = new System.Drawing.Font("Tahoma", 12f),
                    LabelStyleFont = new System.Drawing.Font("Tahoma", 10f),
                    ChartTitleFont = new System.Drawing.Font("Arial", 16f, System.Drawing.FontStyle.Bold),
                    LegendsFont = new System.Drawing.Font("Tahoma", 12f)
                };

            return new PdfReport().DocumentPreferences(doc =>
            {
                doc.RunDirection(PdfRunDirection.RightToLeft);
                doc.Orientation(PageOrientation.Portrait);
                doc.PageSize(PdfPageSize.A4);
                doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
            })
             .DefaultFonts(fonts =>
             {
                 fonts.Path(string.Format("{0}\\fonts\\irsans.ttf", AppPath.ApplicationPath),
                            string.Format("{0}\\fonts\\verdana.ttf", Environment.GetEnvironmentVariable("SystemRoot")));
             })
             .PagesFooter(footer =>
             {
                 footer.DefaultFooter(printDate: DateTime.Now.ToString("MM/dd/yyyy"));
             })
             .PagesHeader(header =>
             {
                 header.DefaultHeader(defaultHeader =>
                 {
                     defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                     defaultHeader.Message("گزارش جدید ما");
                 });
             })
             .MainTableTemplate(template =>
             {
                 template.BasicTemplate(BasicTemplate.ClassicTemplate);
             })
             .MainTablePreferences(table =>
             {
                 table.ColumnsWidthsType(TableColumnWidthType.Relative);
             })
             .MainTableDataSource(dataSource =>
             {
                 var listOfRows = new List<User>();
                 for (var i = 0; i < 7; i++)
                 {
                     listOfRows.Add(new User { Id = i, LastName = "نام خانوادگی " + i, Name = "نام " + i, Balance = (i * 50) + 1000 });
                 }
                 dataSource.StronglyTypedList(listOfRows);
             })
             .MainTableEvents(events =>
             {
                 events.DataSourceIsEmpty(message: "There is no data available to display.");
                 events.DocumentOpened(args =>
                 {
                     chart.ChartInit(width: (int)args.PdfWriter.PageSize.Width - 100, height: 250);
                 });
                 events.RowAdded(args =>
                 {
                     if (args.RowType == RowType.DataTableRow)
                     {
                         var name = args.TableRowData.GetValueOf<User>(x => x.Name);
                         if (name == null) return;

                         var balance = args.TableRowData.GetValueOf<User>(x => x.Balance);
                         if (balance == null) return;

                         chart.AddXY(name, balance);
                     }
                 });
                 events.DocumentClosing(args =>
                 {
                     chart.AddChartToPage(args.PdfDoc);
                     chart.FreeResources();
                 });
             })
             .MainTableSummarySettings(summary =>
             {
                 summary.OverallSummarySettings("جمع");
                 summary.PreviousPageSummarySettings("نقل از صفحه قبل");
             })
             .MainTableColumns(columns =>
             {
                 columns.AddColumn(column =>
                 {
                     column.PropertyName("rowNo");
                     column.IsRowNumber(true);
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(0);
                     column.Width(1);
                     column.HeaderCell("ردیف", captionRotation: 90);
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName<User>(x => x.Id);
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(1);
                     column.Width(2);
                     column.HeaderCell("شماره");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName<User>(x => x.Name);
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(2);
                     column.Width(2);
                     column.HeaderCell("نام");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName<User>(x => x.LastName);
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(3);
                     column.Width(3);
                     column.HeaderCell("نام خانوادگی");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName<User>(x => x.Balance);
                     column.HeaderCell("موجودی");
                     column.ColumnItemsTemplate(template =>
                     {
                         template.TextBlock();
                         template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                     });
                     column.Width(2);
                     column.AggregateFunction(aggregateFunction =>
                     {
                         aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                         aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                     });
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(4);
                 });
             })
             .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptChartSample.pdf"));
        }
    }
}
برای تهیه این گزارش و افزودن نمودار به آن، از کلاس کمکی ذیل استفاده شده است:
using System.Drawing;
using System.IO;
//It's part of the .NET 4.0+ now.
using System.Windows.Forms.DataVisualization.Charting;
using iTextSharp.text;
using iTextSharp.text.pdf;
using PdfRpt.Core.Helper;

namespace PdfReportSamples.ChartImage
{
    public class MSChartHelper
    {
        // MS Chart learning tutorials:
        // http://weblogs.asp.net/scottgu/archive/2010/02/07/built-in-charting-controls-vs-2010-and-net-4-series.aspx
        Chart _chart;

        public System.Drawing.Font AxisTitleFont { set; get; }

        public string AxisXTitle { set; get; }

        public string AxisYTitle { set; get; }

        public string ChartTitle { set; get; }

        public System.Drawing.Font ChartTitleFont { set; get; }

        public System.Drawing.Font LabelStyleFont { set; get; }

        public System.Drawing.Font LegendsFont { set; get; }

        public void AddChartToPage(Document pdfDoc,
                                   int scalePercent = 100,
                                   float spacingBefore = 20,
                                   float spacingAfter = 10,
                                   float widthPercentage = 80)
        {
            using (var chartimage = new MemoryStream())
            {
                _chart.SaveImage(chartimage, ChartImageFormat.Bmp); //BMP gives the best compression result

                var iTextSharpImage = PdfImageHelper.GetITextSharpImageFromByteArray(chartimage.GetBuffer());
                iTextSharpImage.ScalePercent(scalePercent);
                iTextSharpImage.Alignment = Element.ALIGN_CENTER;

                var table = new PdfPTable(1)
                {
                    WidthPercentage = widthPercentage,
                    SpacingBefore = spacingBefore,
                    SpacingAfter = spacingAfter
                };
                table.AddCell(iTextSharpImage);

                pdfDoc.Add(table);
            }
        }

        public void AddXY(object xValue, params object[] yValue)
        {
            _chart.Series[0].Points.AddXY(xValue, yValue);
        }

        public void ChartInit(int width, int height)
        {
            _chart = new Chart
            {
                Width = width,
                Height = height,
                AntiAliasing = AntiAliasingStyles.All,
                TextAntiAliasingQuality = TextAntiAliasingQuality.High,
                Palette = ChartColorPalette.BrightPastel,
                BackColor = ColorTranslator.FromHtml("#F3DFC1"),
                BackGradientStyle = GradientStyle.TopBottom
            };

            setBorder();
            setTitles();
            setChartAreas();
            setLegends();
            setSeries();
        }

        public void FreeResources()
        {
            if (_chart != null && !_chart.IsDisposed)
                _chart.Dispose();
        }        

        private void setBorder()
        {
            _chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            _chart.BorderlineWidth = 2;
            _chart.BorderlineColor = Color.FromArgb(181, 64, 1);
            _chart.BorderlineDashStyle = ChartDashStyle.Solid;
        }

        private void setChartAreas()
        {
            _chart.ChartAreas.Add("ChartArea1");
            _chart.ChartAreas[0].AxisX.Title = AxisXTitle;
            _chart.ChartAreas[0].AxisY.Title = AxisYTitle;
            _chart.ChartAreas[0].AxisX.TitleFont = AxisTitleFont;
            _chart.ChartAreas[0].AxisY.TitleFont = AxisTitleFont;
            _chart.ChartAreas[0].AxisX.LabelStyle.Font = LabelStyleFont;
            _chart.ChartAreas[0].AxisX.LabelStyle.Angle = -90;
            _chart.ChartAreas[0].BackColor = Color.White;
            _chart.ChartAreas[0].AxisX.LineColor = Color.FromArgb(64, 64, 64);
            _chart.ChartAreas[0].AxisX.MajorGrid.LineColor = Color.FromArgb(64, 64, 64);
            _chart.ChartAreas[0].AxisY.LineColor = Color.FromArgb(64, 64, 64);
            _chart.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.FromArgb(64, 64, 64);
        }

        private void setLegends()
        {
            _chart.Legends.Add("Default");
            _chart.Legends[0].LegendStyle = LegendStyle.Row;
            _chart.Legends[0].IsTextAutoFit = false;
            _chart.Legends[0].DockedToChartArea = "ChartArea1";
            _chart.Legends[0].Docking = Docking.Bottom;
            _chart.Legends[0].IsDockedInsideChartArea = false;
            _chart.Legends[0].BackColor = Color.Transparent;
            _chart.Legends[0].Font = LegendsFont;
        }

        private void setSeries()
        {
            _chart.Series.Add("");
            _chart.Series[0].ChartType = SeriesChartType.Column;
            _chart.Series[0].Palette = ChartColorPalette.EarthTones;
            _chart.Series[0].IsValueShownAsLabel = true;
            _chart.Series[0].IsVisibleInLegend = false;
        }

        private void setTitles()
        {
            _chart.Titles.Add(ChartTitle);
            _chart.Titles[0].Font = ChartTitleFont;
            _chart.Titles[0].TextStyle = TextStyle.Shadow;
            _chart.Titles[0].ShadowOffset = 3;
            _chart.Titles[0].ShadowColor = Color.FromArgb(32, 0, 0);
            _chart.Titles[0].Alignment = ContentAlignment.TopCenter;
            _chart.Titles[0].ForeColor = Color.FromArgb(26, 59, 105);
        }
    }
}

توضیحات:
- استفاده از MSChart در اینجا از این جهت مناسب است که فراگیری کار کردن با آن عمومی بوده و در پروژه‌های وب و ویندوز کاربرد دارد و احتمالا هم اکنون با نحوه کارکردن با آن آشنا هستید، زیرا از سال 2010 به دات نت اضافه شده است.
- در این بین تنها متد جدید و مهم کلاس MSChartHelper، متد AddChartToPage آن است. به کمک متد chart.SaveImage می‌توان تصویر نهایی معادل یک نمودار را در حافظه ذخیره کرد. سپس با استفاده از متد PdfImageHelper.GetITextSharpImageFromByteArray، این تصویر موجود در حافظه را به معادل قابل استفاده آن در iTextSharp تبدیل کرده و به صفحه اضافه خواهیم کرد.
- در کدهای اصلی تولید گزارش، مقدار دهی کلاس کمکی MSChartHelper در قسمت رخدادهای قابل استفاده PdfReport در متد MainTableEvents آن انجام شده است:
در روال رویدادگردان DocumentOpened، بر اساس عرض واقعی صفحه، عرض نمودار را مشخص می‌کنیم:
                 events.DocumentOpened(args =>
                 {
                     chart.ChartInit(width: (int)args.PdfWriter.PageSize.Width - 100, height: 250);

                 });
سپس در روال رویدادگردان RowAdded، فرصت خواهیم داشت به اطلاعات در حال افزوده شدن به گزارش دسترسی داشته باشیم. این اطلاعات را به متد افزودن XY نمودار ارسال خواهیم کرد:
                 events.RowAdded(args =>
                 {
                     if (args.RowType == RowType.DataTableRow)
                     {
                         var name = args.TableRowData.GetValueOf<User>(x => x.Name);
                         if (name == null) return;

                         var balance = args.TableRowData.GetValueOf<User>(x => x.Balance);
                         if (balance == null) return;

                         chart.AddXY(name, balance);
                     }
                 });
و در آخر، پیش از بسته شدن فایل PDF تولیدی (DocumentClosing)، نمودار نهایی را به صفحه اضافه کرده و منابع مرتبط با آن‌را آزاد خواهیم کرد:
                 events.DocumentClosing(args =>
                 {
                     chart.AddChartToPage(args.PdfDoc);
                     chart.FreeResources();
                 });
بنابراین اگر این سؤال عمومی وجود دارد که آیا می‌توان در این بین، به ابتدا و انتهای گزارش اشیایی را افزود، روش کلی آن‌را در روال‌های فوق ملاحظه می‌کنید. توسط args.PdfDoc و args.PdfWriter می‌توان به زیرساخت iTextSharp دسترسی یافت.
 

اشتراک‌ها
مقدمه‌ای بر Min.io: یک Object Storage قدرتمند
با توجه به افزایش سالانه‌ی حجم فایل‌های تحت مدیریت شرکت‌ها، نرم‌افزارها و وب‌سایت‌ها، این روزها مدیریت فایل‌ بخش جدایی‌ناپذیری از چالش‌های پروژه‌های نرم‌افزاری شده است. با افزایش تنوع فایل‌ها و بالا رفتن حجم کلی سیستم، مدیریت فایل‌ها به سادگی می‌تواند گلوگاه و پاشنه‌ی آشیل تمام سیستم‌های نرم‌افزاری موجود باشد. برای حل این مشکل برخی از شرکتها سراغ گزینه‌های آماده مثل amazon s3 میرن اما به دلیل بیشماری میشه به گزینه‌هایی فکر کرد که امکان نصب بر روی سرورهای سازمان رو دارند. یکی از بهترین گزینه‌ها برای مدیریت اشیا نرم‌افزار Min.io هست.
مقدمه‌ای بر Min.io: یک Object Storage قدرتمند
مطالب
ELMAH 1.1

نگارش جدید ELMAH ارائه شده است. این نگارش کاملا با نگارش قبلی سازگار بوده و تنها بازنویسی فایل dll آن با نگارش جدید کفایت می‌کند.

لیست موارد فیکس شده
- System.Data.SQLite.dll causes image problems on 64-bit IIS/ASP.NET
- Incorrect jscript filter sample

لیست تازه‌های آن
- Ability to set mail priority in errorMail config section
- Allow configuration-based filtering rules to be diffrent for e-mail and log
- Adjust behavior of smtpPort attribute in
- Remove the XML declaration from the string returned by ErrorXml.EncodeString


دریافت از گوگل کد و یا دریافت از رپید شیر (سورس +‌ بایناری)

مطالب
تبدیل HTML فارسی به PDF با استفاده از افزونه‌ی XMLWorker کتابخانه‌ی iTextSharp
پیشتر مطلبی را در مورد «تبدیل HTML به PDF با استفاده از کتابخانه‌ی iTextSharp» در این سایت مطالعه کرده‌اید. این مطلب از افزونه HTMLWorker کتابخانه iTextSharp استفاده می‌کند که ... مدتی است توسط نویسندگان این مجموعه منسوخ شده اعلام گردیده و دیگر پشتیبانی نمی‌شود.
کتابخانه جایگزین آن‌را افزونه XMLWorker معرفی کرده‌اند که توانایی پردازش CSS و HTML بهتر و کاملتری را نسبت به HTMLWorker ارائه می‌دهد. این کتابخانه نیز همانند HTMLWorker پشتیبانی توکاری از متون راست به چپ و یونیکد فارسی، ندارد و نیاز است برای نمایش صحیح متون فارسی در آن، نکات خاصی را اعمال نمود که در ادامه بحث آن‌ها را مرور خواهیم کرد.

ابتدا برای دریافت آخرین نگارش‌های iTextSharp و افزونه XMLWorker آن به آدرس‌های ذیل مراجعه نمائید:

تهیه یک UnicodeFontProvider

Encoding پیش فرض قلم‌ها در XMLWorker مساوی BaseFont.CP1252 است؛ که از حروف یونیکد پشتیبانی نمی‌کند. برای رفع این نقیصه نیاز است یک منبع تامین قلم سفارشی را برای آن ایجاد نمود:
    public class UnicodeFontProvider : FontFactoryImp
    {
        static UnicodeFontProvider()
        {
            // روش صحیح تعریف فونت   
            var systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
            FontFactory.Register(Path.Combine(systemRoot, "fonts\\tahoma.ttf"));
            // ثبت سایر فونت‌ها در اینجا
            //FontFactory.Register(Path.Combine(Environment.CurrentDirectory, "fonts\\irsans.ttf"));
        }

        public override Font GetFont(string fontname, string encoding, bool embedded, float size, int style, BaseColor color, bool cached)
        {
            if (string.IsNullOrWhiteSpace(fontname))
                return new Font(Font.FontFamily.UNDEFINED, size, style, color);
            return FontFactory.GetFont(fontname, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, size, style, color);
        }
    }
قلم‌های مورد نیاز را در سازنده کلاس به نحوی که مشاهده می‌کنید، ثبت نمائید.
مابقی مسایل آن خودکار خواهد بود و هر زمانیکه نیاز به قلم خاصی از طرف XMLWorker وجود داشت، به متد GetFont فوق مراجعه کرده و اینبار قلمی با BaseFont.IDENTITY_H را دریافت می‌کند. IDENTITY_H در استاندارد PDF، جهت مشخص ساختن encoding قلم‌هایی با پشتیبانی از یونیکد بکار می‌رود.


تهیه منبع تصاویر

در XMLWorker اگر تصاویر با http شروع نشوند (دریافت تصاویر وب آن خودکار است)، آن تصاویر را از مسیری که توسط پیاده سازی کلاس AbstractImageProvider مشخص خواهد شد، دریافت می‌کند که نمونه‌ای از پیاده سازی آن‌را در ذیل مشاهده می‌کنید:
    public class ImageProvider : AbstractImageProvider
    {
        public override string GetImageRootPath()
        {
            var path = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
            return path + "\\"; // مهم است که این مسیر به بک اسلش ختم شود تا درست کار کند
        }
    }


نحوه تعریف یک فایل CSS خارجی

    public static class XMLWorkerUtils
    {
        /// <summary>
        /// نحوه تعریف یک فایل سی اس اس خارجی
        /// </summary>
        public static ICssFile GetCssFile(string filePath)
        {
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                return XMLWorkerHelper.GetCSS(stream);
            }
        }
    }
برای مسیردهی یک فایل CSS در کتابخانه XMLWorker می‌توان از کلاس فوق استفاده کرد.


تبدیل المان‌های HTML پردازش شده به یک لیست PDF ایی

تهیه مقدمات فارسی سازی و نمایش راست به چپ اطلاعات در کتابخانه XMLWorker از اینجا شروع می‌شود. در حالت پیش فرض کار آن، المان‌های HTML به صورت خودکار Parse شده و به صفحه اضافه می‌شوند. به همین دلیل دیگر فرصت اعمال خواص RTL به المان‌های پردازش شده دیگر وجود نخواهد داشت و به صورت توکار نیز این مسایل درنظر گرفته نمی‌شود. به همین دلیل نیاز است که در حین پردازش المان‌های HTML و تبدیل آن‌ها به معادل المان‌های PDF، بتوان آن‌ها را جمع آوری کرد که نحوه انجام آن‌را با پیاده سازی اینترفیس IElementHandler در ذیل مشاهده می‌کنید:
    /// <summary>
    /// معادل پی دی افی المان‌های اچ تی ام ال را جمع آوری می‌کند
    /// </summary>
    public class ElementsCollector : IElementHandler
    {
        private readonly Paragraph _paragraph;

        public ElementsCollector()
        {
            _paragraph = new Paragraph
            {
                Alignment = Element.ALIGN_LEFT  // سبب می‌شود تا در حالت راست به چپ از سمت راست صفحه شروع شود
            };
        }

        /// <summary>
        /// این پاراگراف حاوی کلیه المان‌های متن است
        /// </summary>
        public Paragraph Paragraph
        {
            get { return _paragraph; }
        }

        /// <summary>
        /// بجای اینکه خود کتابخانه اصلی کار افزودن المان‌ها را به صفحات انجام دهد
        /// قصد داریم آن‌ها را ابتدا جمع آوری کرده و سپس به صورت راست به چپ به صفحات نهایی اضافه کنیم
        /// </summary>
        /// <param name="htmlElement"></param>
        public void Add(IWritable htmlElement)
        {
            var writableElement = htmlElement as WritableElement;
            if (writableElement == null)
                return;

            foreach (var element in writableElement.Elements())
            {
                fixNestedTablesRunDirection(element);
                _paragraph.Add(element);
            }
        }

        /// <summary>
        /// نیاز است سلول‌های جداول تو در توی پی دی اف نیز راست به چپ شوند
        /// </summary>        
        private void fixNestedTablesRunDirection(IElement element)
        {
            var table = element as PdfPTable;
            if (table == null)
                return;

            table.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
            foreach (var row in table.Rows)
            {
                foreach (var cell in row.GetCells())
                {
                    cell.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
                    foreach (var item in cell.CompositeElements)
                    {
                        fixNestedTablesRunDirection(item);
                    }
                }
            }
        }
    }
این کلاس کلیه المان‌های دریافتی را به یک پاراگراف اضافه می‌کند. همچنین اگر به جدولی در این بین برخورد، مباحث RTL آن‌را نیز اصلاح خواهد نمود.


یک مثال کامل از نحوه کنار هم قرار دادن پیشنیازهای تهیه شده

خوب؛ تا اینجا یک سری پیشنیاز را تهیه کردیم، اما XMLWorker از وجود آن‌ها بی‌خبر است. برای معرفی آن‌ها باید به نحو ذیل عمل کرد:
            using (var pdfDoc = new Document(PageSize.A4))
            {
                var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("test.pdf", FileMode.Create));
                pdfWriter.RgbTransparencyBlending = true;
                pdfDoc.Open();


                var html = @"<span style='color:blue; font-family:tahoma;'><b>آزمایش</b></span>   
                                    کتابخانه <i>iTextSharp</i> <u>جهت بررسی فارسی نویسی</u>
                            <table style='color:blue; font-family:tahoma;' border='1'><tr><td>eeمتن</td></tr></table>
                            <code>This is a code!</code>
                            <br/>
                            <img src='av-13489.jpg' />
                            ";

                var cssResolver = new StyleAttrCSSResolver();
                // cssResolver.AddCss(XMLWorkerUtils.GetCssFile(@"c:\path\pdf.css"));
                cssResolver.AddCss(@"code 
                                     {
                                        padding: 2px 4px;
                                        color: #d14;
                                        white-space: nowrap;
                                        background-color: #f7f7f9;
                                        border: 1px solid #e1e1e8;
                                     }",
                                     "utf-8", true);

                // کار جمع آوری المان‌های ترجمه شده به المان‌های پی دی اف را انجام می‌دهد
                var elementsHandler = new ElementsCollector();

                var htmlContext = new HtmlPipelineContext(new CssAppliersImpl(new UnicodeFontProvider()));
                htmlContext.SetImageProvider(new ImageProvider());
                htmlContext.CharSet(Encoding.UTF8);
                htmlContext.SetAcceptUnknown(true).AutoBookmark(true).SetTagFactory(Tags.GetHtmlTagProcessorFactory());
                var pipeline = new CssResolverPipeline(cssResolver,
                                                       new HtmlPipeline(htmlContext, new ElementHandlerPipeline(elementsHandler, null)));
                var worker = new XMLWorker(pipeline, parseHtml: true);
                var parser = new XMLParser();
                parser.AddListener(worker);
                parser.Parse(new StringReader(html));

                // با هندلر سفارشی که تهیه کردیم تمام المان‌های اچ تی ام ال به المان‌های پی دی اف تبدیل شدند
                // الان تنها کافی کافی است تا این‌ها را در یک جدول راست به چپ محصور کنیم تا درست نمایش داده شوند
                var mainTable = new PdfPTable(1) { WidthPercentage = 100, RunDirection = PdfWriter.RUN_DIRECTION_RTL };
                var cell = new PdfPCell
                {
                    Border = 0,
                    RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                    HorizontalAlignment = Element.ALIGN_LEFT
                };
                cell.AddElement(elementsHandler.Paragraph);
                mainTable.AddCell(cell);

                pdfDoc.Add(mainTable);
            }

            Process.Start("test.pdf");
نحوه تعریف inline css یا نحوه افزودن یک فایل css خارجی را نیز در ابتدای این مثال مشاهده می‌کنید.
UnicodeFontProvider باید به HtmlPipelineContext شناسانده شود.
ImageProvider توسط متد SetImageProvider به HtmlPipelineContext معرفی می‌شود.
ElementsCollector سفارشی ما در قسمت CssResolverPipeline باید به سیستم تزریق شود.
پس از آن XMLWorker را وادار می‌کنیم تا HTML را Parse کرده و معادل المان‌های PDF ایی آن‌را تهیه کند؛ اما آن‌ها را به صورت خودکار به صفحات فایل PDF نهایی اضافه نکند. در این بین ElementsCollector ما این المان‌ها را جمع آوری کرده و در نهایت، پاراگراف کلی حاصل از آن‌را به یک جدول با RUN_DIRECTION_RTL اضافه می‌کنیم. حاصل آن نمایش صحیح متون فارسی است.

کدهای مثال فوق را از آدرس ذیل نیز می‌توانید دریافت کنید:
XMLWorkerRTLsample.cs


به روز رسانی
کلیه نکات مطلب فوق را به همراه بهبودهای مطرح شده در نظرات آن، در پروژه‌ی ذیل می‌توانید به صورت یکجا دریافت و بررسی کنید:
XMLWorkerRTLsample.zip
نظرات مطالب
تولید فایل‌های اکسل حرفه‌ای بدون نیاز به نصب مجموعه‌ی آفیس
- فایل‌های استاندارد آن روی Any CPU کامپایل شده‌اند. ادامه در اینجا
+ وابستگی WindowsBase هم یک فایل استاندارد مجموعه دات نت است و نه خارج از آن و در WPF از آن استفاده می‌شود. بنابراین مشکلی ندارد (چون حتی اگر یک dll بومی ویندوز هم باشد، با توجه به اینکه عملا دو نگارش X86 و X64 دات نت وجود دارند، بسته به سیستم مورد استفاده، یکی از آن‌ها به صورت خودکار در ابتدای کار نصب دات نت فریم ورک، نصب خواهد شد).
مطالب
توسعه برنامه های Cross Platform با Xamarin Forms & Bit Framework - قسمت چهارم
تا قسمت سوم توانستیم Xamarin را نصب و پروژه‌ی اولیه آن را بیلد کنیم. سپس کد مشترک بین سه پلتفرم را بر روی Windows اجرا و Edit & continue آن را هم تست کردیم که هم برای UI ای که با Xaml نوشته می‌شود و هم برای منطقی که با CSharp نوشته می‌شود، کار می‌کند.
همانطور که گفتیم، کد UI و Logic برای هر سه پلتفرم مشترک است؛ منتهی به علت امکانات دیباگ فوق العاده و سرعت بیشتر ویندوز، ابتدا آن را بر روی ویندوز تست کردیم و بعد برای تکمیل UI، آن را بر روی Android اجرا می‌کنیم. این بار می‌توانید دو پروژه UWP و iOS را Unload کنید و سپس پروژه Android ای را در صورت Unload بودن Load کنید. (با راست کلیک نمودن روی پروژه). این کار باعث می‌شود Visual Studio بیهوده کند نشود؛ مخصوصا اگر سیستم شما ضعیف است.
ابتدا با موبایل یا تبلت اندرویدی شروع می‌کنیم. اگر چه Xamarin از نسخه‌ی 4.0.3 اندروید به بالا را پشتیبانی می‌کند، ولی توصیه می‌کنم وقتتان را بر روی گوشی‌های اندرویدی کمتر از 4.4 تلف نکنید. دستگاه را می‌توانید، هم به صورت USB و هم به صورت Wifi استفاده کنید. ابتدا باید دستگاه اندرویدی خود را آماده‌ی برای دیباگ کنید. برای این منظور مقاله‌های فارسی و انگلیسی زیادی وجود دارند که می‌توانید از آن‌ها استفاده کنید. من عبارت "اندروید debug" را جستجو کردم و به این مقاله رسیدم. همچنین Android SDK شما باید USB debugging اش نصب شده باشد که البته حجم زیادی ندارد. برای بررسی این مورد ابتدا از وجود فولدر extras\google\usb\_driver درAndroid SDK خود مطمئن شوید. حال سؤال این است که ویژوال استودیو، Android SDK را کجا نصب کرده‌است که خیلی ساده در این لینک توضیح داده شده‌است.
اگر فولدر extras\google\usb\_driver وجود نداشت، باید آن را نصب کنید که خیلی ساده توسط Android SDK Manager امکان پذیر است؛ ولی نه! امکان پذیر نیست!
دلیل: در Xamarin شما همیشه بر روی آخرین SDK‌ها حرکت می‌کنید. این شامل Windows SDK 17134 و Android SDK 27 و iOS SDK 11 است. وقتی از نسخه‌ی فعلی ویژوال استودیو، یعنی 15.8 به نسخه‌ی بعدی ویژوال استودیو که الان Preview است بروید، یعنی 15.9، عملا به این معنا است که به Windows SDK 17763 و Android SDK 28 و iOS SDK 12 می‌روید. این بزرگترین مزیت Xamarin است و این یعنی شما همیشه به صد در صد امکانات هر پلتفرم در زبان CSharp دسترسی دارید و همیشه آخرین SDK هر سیستم عامل در اختیار شماست و اگر دوستی از طریق Swift توانست مثالی از ARKit 2.0 را در iOS 12 پیاده سازی کند، قطعا شما هم می‌توانید. همچنین تیم Xamarin نه تنها این امکانات را بلکه Documentation لازم را نیز در اختیار شما قرار می‌دهد. چون در همین مثال، مستندات Apple به زبان Swift / Objective-C بوده و مستندات Xamarin به زبان CSharp.
حال اگر سری به فولدر Android SDK نصب شده‌ی توسط Visual Studio بزنید، مشاهده می‌کنید که خبری از Android SDK Manager نیست! به صورت رسمی، مدتی است که گوگل در نسخه‌های اخیر Android SDK، دیگر Android SDK Manager را ارائه نمی‌کند و همانطور که گفتم شما الان بر روی آخرین نسخه‌ی Android SDK هستید. هر چند ترفندهایی وجود دارد که این Manager را باز می‌گردانند، ولی لزومی به انجام این کار در Xamarin نیست و شما می‌توانید از Android SDK Manager ای که تیم Xamarin ارائه داده‌است، استفاده کنید. همین مسئله در مورد Android Virtual Device Manager که برای مدیریت Emulator‌ها بود نیز صدق می‌کند.
برای استفاده از این دو، ضمن استفاده از ابزارهای دور زدن تحریم، در ویژوال استودیو، در منوی Tools، به قسمت Android رفته و Android SDK Manager را باز کنید. Android Emulator Manager نیز جایگزین Android Virtual Device Manager ای است که قبلا توسط گوگل ارائه می‌شد. حال بعد از باز کردن Android SDK Manager ارائه شده توسط Xamarin، به برگه‌ی Tools آن بروید و از  قسمت extras مطمئن شوید که Google USB driver تیک خورده باشد.
حال پس از وصل کردن گوشی یا تبلت اندرویدی به سیستم توسط کابل USB و Set as startup project نمودن پروژه‌ی XamApp.Android که در قسمت قبل آن را Clone کرده بودید، می‌توانید پروژه را بر روی گوشی خود اجرا کنید. اگر نام گوشی خود را در کنار دکمه‌ی سبز اجرای پروژه (F5) نمی‌بینید، بستن و باز کردن Visual Studio را امتحان کنید. 

پروژه را که اجرا کنید، اولین بیلد کمی طول می‌کشد (اولین بار دو برنامه بر روی گوشی شما نصب می‌شوند که برای کار دیباگ در Xamarin لازم هستند) و اساسا بیلد یک پروژه‌ی اندرویدی کند است. خوشبختانه به واسطه وجود Xaml edit and continue احتیاجی به Stop - Start کردن پروژه و بیلد کردن برای اعمال تغییرات UI نیست و به محض تغییر Xaml، می‌توانید تاثیر آن را در گوشی خود ببینید. ولی برای هر تغییر CSharp باید Stop - Start و Build کنید که زمان بر است و به همین علت تست بر روی پروژه ویندوزی را برای پیاده سازی منطق برنامه پیشنهاد می‌کنیم. البته در نسخه‌ی 15.9 ویژوال استودیو، سرعت بیلد تا 40% بهبود یافته است.
ممکن است شما گوشی اندرویدی یا تبلت نداشته باشید که بخواهید بر روی آن تست کنید و یا مثلا گوشی شما Android 7 هست و می‌خواهید بر روی Android 8 تست بگیرید. در این جا شما احتیاج به استفاده از Emulator را خواهید داشت.
توجه داشته باشید که Emulator شما ترجیحا نباید ARM باشد و بهتر است یا X86 یا X64 باشد، وگرنه ممکن است خیلی کند شود. همچنین بهتر است Google Play Services داشته باشد. همچنین ترجیحا دنبال گزینه‌ی اجرا کردن Emulator نروید؛ اگر خود ویندوز شما درون یک Virtual Machine در حال اجراست.

ابتدا ضمن جستجو کردن "فعال سازی intel virtualization"، اقدام به فعال سازی این امکان در سیستم خود کنید. این آموزش را مناسب دیدم.
گزینه‌های مطرح: [Google Android Emulator] - [Genymotion] - [Microsoft Hyper-V Android Emulator] که فقط یکی از آنها را لازم دارید.

Google Android Emulator توسط خود Google ارائه می‌شود و دارای Google Play Services نیز هست. بر اساس این آموزش به صفحه Workloads در Visual Studio Installer بروید و از قسمت Xamarin دو مورد "Google Android Emulator API Level 27" و "Intel Hardware Accelerated Execution Manager (HAXM) global install" را نصب کنید. توجه داشته باشید که بدین منظور احتیاج به ابزارهای دور زدن تحریم دارید؛ زیرا نیاز به دسترسی به سرورهای گوگل هست. این Emulator آماده برای دیباگ هست و نیازی به اقدام خاصی نیست.

Genymotion حجم کمتری دارد و برای دانلود احتیاج به ابزارهای دور زدن تحریم را ندارد و اساسا نسبت به بقیه بر روی سیستم‌های ضعیف‌تر، بهتر کار می‌کند. فقط Emulator ای که با آن می‌سازید، به صورت پیش فرض Google Play Services را ندارد که در آخرین نسخه‌های آن گزینه Open  GApps به toolbar اضافه شده که Google Play Services را اضافه می‌کند. (از انجام هر گونه عملیات پیچیده بر اساس آموزش‌هایی که برای نسخه‌های قدیمی‌تر Genymotion هستند، پرهیز کنید). مطابق با ابتدای همین آموزش برای دستگاه‌های اندرویدی، Emulator خود را آماده برای دیباگ کنید.

Microsoft Hyper-V android emulators. مایکروسافت قبلا اقدام به ارائه یک Android Emulator کرده بود که برای نسخه 4 و 5 اندروید بودند و بزرگ‌ترین ضعف آنها عدم پشتیبانی از Google Play Services بود که ادامه داده نشدند. ولی سری جدید ارائه شده توسط مایکروسافت چنین مشکلی را ندارد. اگر CPU شما AMD بوده و روش‌های قبلی برای شما کند هستند یا اساسا کار نمی‌کنند، یا در حال حاضر در حال استفاده از Docker for Windows هستید که از Hyper-V استفاده می‌کنید و قصد استفاده مجدد از منابع موجود را دارید، این نیز گزینه خوبی است که جزئیات آن را می‌توانید در  اینجا  دنبال کنید. این Emulator آماده برای دیباگ هست و نیازی به اقدام خاصی نیست. 

پس از اینکه Emulator خود را ساختید، آن را اجرا کنید. سپس برنامه را از درون ویژوال استدیو اجرا کنید. مطابق نسخه ویندوزی، دوباره یک دکمه دارید و یک Label، عدد بر روی Label، با هر بار کلیک کردن بر روی دکمه، افزایش می‌یابد.
سرعت اجرای این برنامه در Emulator یا گوشی شما برای دیباگ است و در حالت Release، سرعت چندین برابر بهتر خواهد شد و به هیچ وجه تست‌های Performance را بر روی Debug mode انجام ندهید.

حال نوبت به پابلیش پروژه می‌رسد. در این قسمت باید توجه کنید که حجم Apk شما برای پروژه‌ی XamApp مثال ما به 7 مگ می‌رسد که برای یک فرم ساده خیلی زیاد به نظر می‌رسد. ولی اگر شما بجای یک فرم ساده، صد فرم پیچیده نیز داشته باشید، باز هم این حجم به 8 مگ نخواهد رسید. حجم Apk خیلی متاثر از کدهای شما نیست، بلکه شامل موارد زیر است:
1- NET. که خود شامل CLR  & BCL است. (BCL (Base Class Library  مثل کلاس‌های string - Stream - List - File و (CLR (Common language runtime که شامل موارد لازم برای اجرای کدها است. این پیاده سازی بر اساس NET Standard 2.0. بوده که عملا اجازه استفاده از تعداد خیلی زیادی از کتابخانه‌های موجود را می‌دهد، حتی Entity framework core! البته هر کتابخانه حجم DLL‌های خودش را اضافه می‌کند.
2- Android Support libraries که به شما اجازه می‌دهد از تعداد زیادی (و البته نه همه) امکانات نسخه‌های جدید اندروید در پروژه‌تان استفاده کنید که بر روی نسخ قدیمی‌تر Android نیز کار کنند. همچنین با یکپارچگی با Google Play Services عملا خیلی از کارها ساده‌تر و با Performance بهتری انجام می‌شود، مانند گرفتن موقعیت کاربر جاری.
3-  Xamarin essentials . اگر چه در CSharp شما به صد در صد امکانات هر سیستم عامل دسترسی دارید و می‌توانید مثلا مقدار درصد شارژ باطری را بخوانید، ولی اینکار مستلزم نوشتن سه کد CSharp ای برای Android - iOS - Windows است که طبیعتا کار را سخت می‌کند. اما Xamarin Essentials به شما اجازه می‌دهد با یک کد CSharp واحد برای هر سه پلتفرم، با باطری، کلیپ‌بورد، قطب نما و خیلی موارد دیگر کار کنید.
4- Xamarin.Forms. اگر Button و Label ای که در مثال برنامه داشتیم، با یکبار نوشتن بر روی هر سه پلتفرم دارند کار می‌کنند، در حالی که هر پلتفرم، Button مخصوص به خود را دارد؛ این را Xamarin Forms مدیریت می‌کند. علاوه بر این، Binding نیز به عهده‌ی Xamarin Forms است.
5- Prism Autofac Bit Framework: درک آن‌ها نیاز به دنبال کردن آموزش‌های این دوره را دارد؛ ولی به صورت کلی معماری پروژه شما بسیار کارآمد و حرفه‌ای خواهد شد و به کدی با قابلیت نگهداری بالا خواهید رسید. 
6-  Rg Plugins Popup  و  Xamanimation  نیز دو کتابخانه‌ی UI بسیار کاربردی و جالب هستند که در طول این آموزش از آنها استفاده خواهد شد.
حجم 7 مگ برای این تعداد کتابخانه و امکان، خیلی زیاد نیست و شما عملا تعداد زیادی از پروژه‌های خود را می‌توانید با همین حجم ببندید و اگر مثلا به پروژه‌ی Humanizer خیلی علاقه داشته باشید (که در این صورت حق هم دارید!) می‌توانید با اضافه شدن چند کیلوبایت (!) به پروژه آن را داشته باشید. اکثر کتابخانه‌های NET. ای سبک هستند. همچنین موقع قرار گرفتن در پروژه، فشرده سازی نیز می‌شوند و قسمت‌های استفاده نشده‌ی آن‌ها نیز توسط Linker حذف می‌شوند.
علاوه بر این، اجرای برنامه بر روی گوشی‌های ضعیف و قدیمی کمی طول می‌کشد. این مربوط به اجرای برنامه است؛ نه باز شدن فرم مثال ما که دارای Button و Label بود و اگر مثال ما دو فرم داشته باشد (که در آموزش‌های بعدی به آن می‌رسیم) می‌بینید که چرخش بین فرم‌ها بسیار سریع است.

مواردی مهم در زمینه‌ی بهبود عملکرد پروژه‌های Xamarin در Android
در ابتدا باید بدانید Apk شما شامل دو قسمت است؛ یکی کدهای CSharp ای شما که DotNet ای بوده و در کنار کدهای کتابخانه‌هایی چون Json.NET بر روی DotNet اجرا می‌شوند. دیگری کتابخانه‌ای است که مثلا با Java نوشته شده و بعد برای استفاده در CSharp بر روی آن یک Wrapper یا پوشاننده توسعه داده شده‌است. عموما توسعه دهندگان چنین پروژه‌هایی، ابتدا پروژه را به Java می‌نویسند و بعد برای JavaScript - CSharp و ... Wrapper ارائه می‌دهند.
برای بهبود اینها ابزارهایی چون AOT-NDK-LLVM-ProGurad-Linker و ... وجود دارند که سعی می‌کنم به صورت ساده آنها را توضیح دهم.

وظیفه ProGurad این است که از قسمتی از پروژه‌ی شما که بخاطر کتابخانه‌های Java ای، عملا DotNet ای نیست، کدهای اضافه و استفاده نشده را حذف کند.
ممکن است استفاده از ProGurad باعث شود کلاسی که داینامیک استفاده شده است، به اشتباه حذف شود. پروژه XamApp دارای یک ProGuard configuration file است که جلوی چنین اشتباهاتی را حتی الامکان می‌گیرد.
همچنین ProGurad که در داخل Android SDK قرار دارد، به Space در طول مسیر حساس است (!) و با توجه به اینکه مسیر پیش فرض Android SDK نصب شده‌ی توسط ویژوال استودیو دارای Space است (C:\Program Files (x86)\Android\android-sdk)  شما در همان ابتدا دچار مشکل می‌شوید! برای حل این مشکل ابدا فولدر Android SDK را جا به جا نکنید؛ بلکه از امکانی در ویندوز به نام Junction folder یا فولدر جانشین استفاده کنید. بدین منظور دستور زیر را وارد کنید:
mklink /j C:\android-sdk "C:\Program Files (x86)\Android\android-sdk"
این مورد باعث می‌شود که مسیر C:\android-sdk نیز به همان مسیر پیش فرض اشاره کند و این دو مسیر در واقع یکی هستند. امیدوارم این امکان را با قابلیت Shortcut سازی در ویندوز اشتباه نگیرید! حال از منوی Tools > Options > Xamarin > Android Settings مسیر Android SDK را به C:\android-sdk تغییر دهید که فاقد Space است و ویژوال استودیو را ببندید و باز کنید.

NDK که در ادامه SDK برای Android قرار می‌گیرد، Native Development Kit است و باعث می‌شود هم DLL‌های DotNet ای و هم Jar‌های Java ای به فایل‌های so تبدیل شوند. so همان DLL ویندوز است، البته برای Linux و همانطور که احتمالا می‌دانید، پایه Android بر روی Linux است. طبیعتا کامپایل شدن کدها به so، بر روی بهبود سرعت برنامه تاثیر گذار است.

Linker نیز مشابه با ProGuard کمک می‌کند، ولی اینبار حجم DLL‌های DotNet ای مانند Json.NET را کم می‌کند. بالاخره شما از صد در صد کلاس‌های یک DLL استفاده نمی‌کنید و موارد اضافی نیز باید حذف شوند. البته این وسط، امکان حذف اشتباه کلاس‌هایی که به صورت داینامیک فراخوانی شده باشند وجود دارد که LinkerConfig موجود در پروژه XamApp حتی الامکان جلوی این مشکل را می‌گیرد.

Release mode  مثل هر پروژه CSharp ای دیگری، بهتر است پروژه در حالت Release mode پابلیش شود. در پروژه XamApp در حالت Release mode، موارد بالا یعنی Linker-NDK-ProGuard نیز درخواست می‌شوند.

جزئیات این موارد در مستندات Xamarin وجود دارد و در پایان این دوره یک Project Builder سورس باز نیز به شما ارائه می‌شود که ساختار اولیه پروژه‌ها را بر اساس نیازهای شما و با بهترین تنظیمات ممکن می‌سازد.

در پروژه XamApp علاوه بر موارد فوق، دو مورد دیگر نیز آماده به استفاده هستند، ولی غیر فعال شده اند؛ AOT و LLVM. اگر به تازگی برنامه نویس شده‌اید، موارد زیر ممکن است خیلی برایتان پیچیده باشند، از آن‌ها عبور کنید و به عنوان "نحوه انجام دادن پابلیش" بروید.

کدهای‌های DotNet ای به سه شکل می‌توانند اجرا شوند:
JIT - AOT - Interpreter
یک برنامه DotNet ای برای اجرا می‌تواند از ترکیب اینها استفاده کند. حالت Interpreter که خیلی جدید معرفی شده و الآن موضوع بحث نیست؛ می‌ماند JIT و AOT
کد CSharp در هنگام کامپایل به IL تبدیل و سپس در زمان اجرا توسط Just in time compiler به زبان ماشین تبدیل می‌شود. اگر قبلا پروژه‌ی ASP.NET یا ASP.NET Core نوشته باشید، چنین رفتاری را در پشت صحنه خواهد داشت. خود JIT که در هر بار اجرای برنامه انجام می‌شود، عملا زمان بر هست. ولی کد زبان ماشین حاصل از آن خیلی Optimize شده برای دقیقا همان ماشین هست؛ با در نظر گرفتن خیلی فاکتورها. در پروژه‌های سمت سرور مثل ASP.NET که پروژه وقتی یک بار اجرا می‌شود، مثلا روی IIS، ممکن است صدها هزار دستور را اجرا کند، در طول چندین روز یا ماه، این عمل JIT خیلی مفید هست. البته همان سربار اولیه‌ی JIT هم توسط چیزی به عنوان Tiered JIT می‌تواند کمتر شود.
اما در پروژه‌ی موبایل که برنامه ممکن است بعد از باز شدن، مثلا ده دقیقه باز باشد و بعد بسته شود، انجام شدن JIT با هر بار باز شدن برنامه خیلی مفید به فایده نیست. بنا به برخی مسائل که واقعا سطح این آموزش را خیلی پیچیده می‌کند، نتیجه کار JIT قابلیت Cache شدن آن چنانی ندارد و عملا باید هر بار اجرا شود.
در پروژه‌های موبایل، گزینه دیگری بر روی میز هست به نام Ahead of time یا AOT که کار تبدیل IL به زبان ماشین را در زمان کامپایل و پابلیش پروژه انجام دهد. طبیعتا این باعث می‌شود سرعت برنامه موبایل در عمل خیلی بالاتر رود، چون سربار JIT در هر بار اجرای برنامه حذف می‌شود. همچنین روال AOT می‌تواند از LLVM یا Low level virtual machine استفاده کند که منجر به تبدیل شدن کد زبان ماشینی می‌شود که بر روی LLVM کار می‌کند. LLVM خودش یک Runtime با سرعت خیلی بالاست که بر روی تمامی سیستم عامل‌ها کار می‌کند.
بر روی Android - iOS - Windows می‌شود از AOT استفاده کرد. در iOS و ویندوز، استفاده‌ی از AOT منجر به افزایش سایز برنامه نمی‌شود، چون قبلا برنامه یک سری کد IL بوده که زمان اجرا توسط JIT به کد ماشین تبدیل می‌شده و الان بجای آن IL، یک سری کد زبان ماشین مبتنی بر LLVM هست. اما بر روی Android، پیاده سازی AOT ناقص هست و البته که با فعال کردن‌اش، سرعت برنامه بسیار بیشتر می‌شود، ولی کماکان نیاز به JIT و IL هم برای برخی از سناریوها هست. این مورد یعنی اینکه فعال سازی AOT+LLVM بر روی اندروید تا مادامی که AOT در Android به صورت آزمایشی هست، باعث افزایش حجم Apk ما از 7 به 13 مگ می‌شود. البته این مورد در نسخه‌های بعدی رفع خواهد شد و رفتار Android مشابه با iOS-Windows خواهد بود؛ یعنی حجم نسبتا کم و سرعت خیلی بالا.
برای فعال سازی AOT+LLVM در csproj پروژه اندرویدی، دو مقدار AotAssemblies و EnableLLVM را از false به true تغییر دهید:
 <AotAssemblies>true</AotAssemblies> 
 <EnableLLVM>true</EnableLLVM>
با این تنظیمات، بیلد شما طولانی‌تر و در عوض سرعت اجرای برنامه بیشتر خواهد شد.

نحوه انجام دادن پابلیش 
برای انجام دادن پابلیش، بر روی پروژه XamApp.Android در هنگامیکه بر روی Release mode هستید، راست کلیک کنید و Archive را بزنید. سپس فایل Archive شده را انتخاب و Distribute را بزنید که به شما Apk مناسب برای انتشار توسط خودتان یا Google Play می‌دهد.
نکات مهم:
1- فایل Apk حاصل از Archive را بدون Distribute کردن، در اختیار کسی قرار ندهید. فقط پیام Corrupt و خراب بودن فایل، حاصل کارتان خواهد بود!
2- اولین باری که Distribute می‌کنید، Wizard مربوطه کمک می‌کند تا یک فایل Certificate را برای Apk اتان بسازید. آن فایل را گم نکنید! در پابلیش‌های بعدی دیگر نباید Certificate جدیدی بسازید؛ بلکه فایل قبلی را باید به آن معرفی کنید و فقط رمز آن Certificate را دوباره بزنید.
3- به برنامه آیکون بدهید. برای آن Splash Screen خوبی بگذارید. در هر بار پابلیش، ورژن برنامه را افزایش دهید. اینها همگی توضیحات اش بر روی بستر وب موجود است. سؤالی بود، همینجا هم می‌توانید بپرسید.
فایل‌های Apk این مثال را می‌توانید از اینجا دانلود کنید.

در قسمت بعدی آموزش، دیباگ و پابلیش گرفتن پروژه بر روی iOS را خواهیم داشت که البته مقداری از مطالب اش با مطالب این آموزش مشترک هست. بعد دست به کد شده و آموزش CSharp و Xaml را خواهیم داشت تا پروژه‌ای با کیفیت، کارآمد و عالی از هر جهت بنویسید.
همچنین تعدادی از نکات مربوط به Performance که مربوط به ظاهر برنامه و نحوه چیدمان صفحات و کنترل‌ها هستند و بر روی Performance هر سه پلتفرم تاثیر گذار هستند (و نه فقط Android‌) نیز در ادامه بحث خواهند شد.
اشتراک‌ها
برنامه ریزی برای EF Core 6.0

JSON columns

  • Introduce common patterns for JSON support that can be implemented by any database provider.
  • JSON column support will be implemented for SQL Server and SQLite. (Note that the PostgreSQL and MySQL providers already support JSON columns.) 
برنامه ریزی برای EF Core 6.0