مسیرراه‌ها
ASP.NET MVC
              مطالب
              ایجاد قالب‌های سفارشی ستون‌ها و فرمت شرطی اطلاعات در PdfReport
              صورت مساله:
              - لیستی از حقوق کارکنان را داریم. در گزارش نهایی آن نیاز است عدد حقوق کارکنانی با مبلغ کمتر از 1000، با رنگی دیگر نمایش داده شوند.
              همچنین در این گزارش هر ردیفی که در ماه 7 واقع شده نیز ظاهر عدد سلول مربوط به آن ماه، به رنگ قهوه‌ای و زمینه زرد تغییر یابد.
              - در ستون مشخصات افراد این گزارش، نیاز است تصویر کارمند به همراه نام او در ذیل این تصویر (داخل یک سلول) نمایش داده شوند.

              چیزی شبیه به این گزارش!


              مورد اول در گزارشات، اصطلاحا به conditional formatting معروف است و مورد دوم مرتبط است به تهیه قالب‌های سفارشی، بجای استفاده از قالب‌های سلول‌های پیش فرض PdfReport؛ که در ادامه نحوه انجام این موارد را بررسی خواهیم کرد.

              ابتدا سورس کامل این مثال را ملاحظه نمائید:
              using System;
              using iTextSharp.text;
              using PdfRpt.Core.Contracts;
              using PdfRpt.Core.Helper;
              using PdfRpt.FluentInterface;
              
              namespace PdfReportSamples.CustomCellTemplate
              {
                  public class CustomCellTemplatePdfReport
                  {
                      public IPdfReportData CreatePdfReport()
                      {
                          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" });
                              doc.Compression(new CompressionSettings
                                             {
                                                 CompressionLevel = CompressionLevel.BestCompression,
                                                 EnableCompression = true
                                             });
                          })
                           .DefaultFonts(fonts =>
                           {
                               fonts.Path(AppPath.ApplicationPath + "\\fonts\\irsans.ttf",
                                          Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
                           })
                           .PagesFooter(footer =>
                           {
                               footer.DefaultFooter(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.SnowyPineTemplate);
                           })
                           .MainTablePreferences(table =>
                           {
                               table.ColumnsWidthsType(TableColumnWidthType.Relative);
                               table.MultipleColumnsPerPage(new MultipleColumnsPerPage
                               {
                                   ColumnsGap = 20,
                                   ColumnsPerPage = 2,
                                   ColumnsWidth = 250,
                                   IsRightToLeft = true,
                                   TopMargin = 7
                               });
                           })
                           .MainTableDataSource(dataSource =>
                           {
                               var table = new System.Data.DataTable("لیست حقوق");
                               table.Columns.Add("شخص", typeof(string));
                               table.Columns.Add("ماه", typeof(int));
                               table.Columns.Add("مبلغ", typeof(decimal));
              
                               var rnd = new Random();
                               for (int i = 0; i < 200; i++)
                                   table.Rows.Add("شخص " + i, rnd.Next(1, 12), rnd.Next(400, 2000));
              
                               dataSource.DataTable(table);
                           })
                           .MainTableEvents(events =>
                           {
                               events.DataSourceIsEmpty(message: "There is no data available to display.");
                               events.CellCreated(args =>
                                   {
                                       //change the background color of the cell based on the value
                                       if (args.RowType == RowType.DataTableRow && args.Cell.RowData.Value != null && args.Cell.RowData.Value is decimal)
                                       {
                                           if ((decimal)args.Cell.RowData.Value <= 1000)
                                               args.Cell.BasicProperties.BackgroundColor = BaseColor.CYAN;
                                       }
                                   });
                           })
                           .MainTableSummarySettings(summary =>
                           {
                               summary.OverallSummarySettings("جمع کل");
                               summary.PageSummarySettings("جمع صفحه");
                               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("ردیف");
                               });
              
                               columns.AddColumn(column =>
                               {
                                   column.PropertyName("شخص");
                                   column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                                   column.IsVisible(true);
                                   column.Order(1);
                                   column.Width(3);
                                   column.HeaderCell("شخص");
                                   column.ColumnItemsTemplate(t => t.CustomTemplate(new MyCustomCellTemplate()));
                               });
              
                               columns.AddColumn(column =>
                               {
                                   column.PropertyName("ماه");
                                   column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                                   column.IsVisible(true);
                                   column.Order(2);
                                   column.Width(2);
                                   column.HeaderCell("ماه");
                                   column.ColumnItemsTemplate(template =>
                                   {
                                       template.TextBlock();
                                       template.ConditionalFormatFormula(list =>
                                       {
                                           var cellValue = int.Parse(list.GetSafeStringValueOf("ماه", nullValue: "0"));
                                           if (cellValue == 7)
                                           {
                                               return new CellBasicProperties
                                               {
                                                   PdfFontStyle = DocumentFontStyle.Bold | DocumentFontStyle.Underline,
                                                   FontColor = new BaseColor(System.Drawing.Color.Brown),
                                                   BackgroundColor = new BaseColor(System.Drawing.Color.Yellow)
                                               };
                                           }
                                           return new CellBasicProperties { PdfFontStyle = DocumentFontStyle.Normal };
                                       });
                                   });
                               });
              
                               columns.AddColumn(column =>
                               {
                                   column.PropertyName("مبلغ");
                                   column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                                   column.IsVisible(true);
                                   column.Order(3);
                                   column.Width(2);
                                   column.HeaderCell("مبلغ");
                                   column.ColumnItemsTemplate(template =>
                                   {
                                       template.TextBlock();
                                       template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                                   });
                                   column.AggregateFunction(aggregateFunction =>
                                   {
                                       aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                                       aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                                   });
                               });
                           })
                           .Export(export =>
                               {
                                   export.ToXml();
                                   export.ToExcel();
                               })
                           .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptDataTableSample.pdf"));
                      }
                  }
              }
              به همراه قالب سلول سفارشی آن:
              using System;
              using System.Collections.Generic;
              using iTextSharp.text;
              using iTextSharp.text.pdf;
              using PdfRpt.Core.Contracts;
              using PdfRpt.Core.Helper;
              
              namespace PdfReportSamples.CustomCellTemplate
              {
                  public class MyCustomCellTemplate : IColumnItemsTemplate
                  {
                      Random _rnd = new Random();
              
                      public void CellRendered(PdfPCell cell, Rectangle position, PdfContentByte[] canvases, CellAttributes attributes)
                      {
                      }
              
                      public CellBasicProperties BasicProperties { set; get; }
                      public Func<IList<CellData>, CellBasicProperties> ConditionalFormatFormula { set; get; }
              
                      public PdfPCell RenderingCell(CellAttributes attributes)
                      {
                          var pdfCell = new PdfPCell();
                          var table = new PdfPTable(1) { RunDirection = PdfWriter.RUN_DIRECTION_RTL };
              
                          var filePath = AppPath.ApplicationPath + "\\Images\\" + _rnd.Next(1, 5).ToString("00") + ".png";
                          var photo = PdfImageHelper.GetITextSharpImageFromImageFile(filePath);
                          table.AddCell(new PdfPCell(photo, fit: false)
                          {
                              Border = 0,                
                              VerticalAlignment = Element.ALIGN_BOTTOM,
                              HorizontalAlignment = Element.ALIGN_CENTER
                          });
              
                          var name = attributes.RowData.TableRowData.GetSafeStringValueOf("شخص");
                          table.AddCell(new PdfPCell(attributes.BasicProperties.PdfFont.FontSelector.Process(name))
                          {
                              Border = 0,
                              HorizontalAlignment = Element.ALIGN_CENTER
                          });
              
                          pdfCell.AddElement(table);
              
                          return pdfCell;
                      }
                  }
              }

              توضیحات:

              - در این مثال از منبع داده‌ای از نوع DataTable استفاده شده است؛ که نحوه بکارگیری آن‌را در متد MainTableDataSource ملاحظه می‌کنید. ستون‌های تعریف شده در MainTableColumns نیز بر اساس ستون‌های DataTable مشخص شده‌اند.
              - در متد DocumentPreferences، نحوه مشخص سازی فشرده سازی نهایی فایل PDF را ملاحظه می‌کنید. این مورد از مزایای استفاده از فایل‌های PDF است.

              - برای اعمال فرمت شرطی اطلاعات در PdfReport دو روش وجود دارد.
              الف) استفاده از متد MainTableEvents و کار کردن با رخ‌دادهای تعریف شده در آن مانند CellCreated. در اینجا می‌توان در نحوه رندر شدن یک سلول دخالت کرد:
              events.CellCreated(args =>
                  {
                       //change the background color of the cell based on the value
                       if (args.RowType == RowType.DataTableRow && args.Cell.RowData.Value != null && args.Cell.RowData.Value is decimal)
                       {
                            if ((decimal)args.Cell.RowData.Value <= 1000)
                                 args.Cell.BasicProperties.BackgroundColor = BaseColor.CYAN;
                        }
                    });
              برای مثال در تعاریف فوق، اگر نوع ردیف، از نوع ردیف‌های اطلاعاتی جدول باشد، مقدار آن دریافت شده و بر اساس شرطی مشخص، برای نمونه رنگ پس زمینه آن سلول تغییر داده می‌شود.
              ب) همانطور که در قسمت تعریف ستون «ماه» ملاحظه می‌کنید، توسط متد template.ConditionalFormatFormula نیز، امکان فرمت شرطی اطلاعات فراهم شده است. در اینجا می‌توان به لیست اطلاعات سلول‌های ردیف جاری دسترسی یافت و سپس بر اساس آن تصمیم گیری کرد.

              - جهت تعریف قالب‌های سفارشی سلول‌ها کافی است اینترفیس IColumnItemsTemplate را پیاده سازی کنیم؛ که نمونه‌ای از آن را در کدهای MyCustomCellTemplate فوق ملاحظه می‌کنید. در اینجا فرصت خواهید داشت هر شکل و طرح متنوعی را تهیه کرده و به صورت یک PdfPCell بازگشت دهید. برای نمونه در مثال فوق، یک جدول را در سلول تعریف شده قرار داده‌ایم. این جدول یک ستون دارد و هر سلولی که به آن اضافه خواهد شد، یک ردیف را تشکیل خواهد داد. در ردیف اول آن تصویر قرار گرفته و در ردیف دوم آن مقدار سلول جاری.
              نظرات مطالب
              اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
               با سلام
              ما در یکی از پروژه هایمان طبق موراد گفته شده کامل پیش رفته ایم و حال در نسخه production ابهاماتی داریم ، ممنون میشوم راهنمایی بفرمایید 
              1- یک کاربر در سیستم لاگین کرد و ما دستی در دیتابیس مقدار فیلد 
              AccessTokenExpiresDateTime

              را طوری تنظیم کردیم که منقضی شده باشد (مثلا ده ساعت به عقب بردیم ) ، در اولین رفرش توکن بعد از این ، انتظار داشتیم که کاربر از سایت خارج شود، ولی یک توکن جدید برای کاربر صادر شد و همچنان در سایت لاگین شده است .
              2--ما مقادیر را مانند زیر ست کرده ایم ، ولی رفرش توکن هر دو دقیقه یک بار اجرا میشود ، آیا این درست است یا باید یک دقیقه یک بار اجرا شود ؟
               "AccessTokenExpirationMinutes": 2,
               "RefreshTokenExpirationMinutes": 1,

              3- در این حالت که توکن‌ها در دیتابیس ذخیره می‌شوند، آیا اگر iis ریست شود  و یا application pool بعد از زمان 20 متوقف شود، تاثیر روی کاربران لاگین شده دارد ؟ 
              پاسخ به بازخورد‌های پروژه‌ها
              نمایش ردیف های اضافه در انتهای هر صفحه
              خیلی متشکرم.
              مثال محاسبه بیمه و مالیات یک فاکتور نیاز من را برآورده می‌کرد ، اگر در هر صفحه تکرار می‌شد. 
              چون table.NumberOfDataRowsPerPage تنظیم شده نمی‌توانم از Footer سفارشی استفاده کنم.
              روش events.RowStartedInjectCustomRows را قبلا امتحان کرده ام ، باید ردیف‌ها بعد از ردیف مجموع باشد. 
              برای این سناریو روشی که به نظرم می‌آید استفاده از روش محاسبه بیمه و مالیات یک فاکتور است اما به این حالت که تعداد رکورد‌های گزارش را تقسیم بر تعداد رکورد در هر صفحه کنم سپس برای هر صفحه یک گزارش تهیه  کنم و داده‌های سلول‌های انتهایی مثل مچموع را خودم حساب کنم، سپس همه‌ی گزارش‌ها را یکی کنم. به نظر شما جوابگو خواهد بود؟
              با تشکر.
              مطالب
              تهیه گزارش در Blazor Wasm با استیمول ریپورت
               جهت تولید گزارش در Blazor Wasm، ابتدا آخرین نسخه‌ی استیمول سافت را از نیوگت دریافت کرده:
               Install-Package Stimulsoft.Reports.Blazor -Version 2021.2.4
              سپس گزارشی را که با DataSource از نوع Business Objects ساخته‌ایم، در مسیر wwwroot/Reports قرار می‌دهیم. انتخاب نوع دیتاسورس اختیاری است و می‌توانیم از سایر دیتاسورس‌ها نیز استفاده کنیم.

              جهت دسترسی به فایل گزارش، نیاز است فایل ریپورت، تبدیل به آرایه‌ای از بایت‌ها شود. به همین دلیل در Web Api یک متد را ساخته و در این متد، فایل گزارش را به آرایه تبدیل می‌کنیم:
              [HttpGet]
              [Route("GetReportFile/{fileName}")]
              public async Task<IActionResult> GetReportFile(string fileName)
              {
                 var rootPath = _webHostEnverioment.WebRootFileProvider.GetDirectoryContents("/")
                                   .FirstOrDefault(x => x.Name == "Reports")?.PhysicalPath;
                 var path = Path.Combine(rootPath!, fileName);
                 var bytes = await System.IO.File.ReadAllBytesAsync(path);
                 return Ok(bytes);
              }
              و سپس در فایل Razor بوسیله HttpClient گزارش را نمایش می‌دهیم:
              @page "/"
              @using Stimulsoft.Base
              @using Stimulsoft.Report
              @using Stimulsoft.Report.Blazor
              @inject HttpClient Http
              <StiBlazorViewer Report="@report" />
              
              @code
              {
                  private StiReport report;
                  protected override async Task OnInitializedAsync()
                  {
                      //Create empty report object
                      report = new StiReport();
                      //Load report template
                      // var reportBytes = await Http.GetByteArrayAsync("Reports/Report.mrt");
                      report.RegBusinessObject("MyList", GetBusinessObject());
                      var uri = $"/api/Report/GetReportFile/Report.mrt";
                      var reportFile=await Http.GetFromJsonAsync<byte[]>(uri);
                    //  var reportFile = await Http.GetByteArrayAsync("Report.mrt");
                      report.Load(reportFile);
                      await report.Dictionary.SynchronizeAsync();
              
                  }
              
                  public class BusinessEntity
                  {
                      public string Name { get; set; }
                      public string Alias { get; set; }
                      public BusinessEntity(string name, string alias)
                      {
                          Name = name;
                          Alias = alias;
                      }
                  }
              
                  private System.Collections.ArrayList GetBusinessObject()
                  {
                      var list = new System.Collections.ArrayList();
                      list.Add(new BusinessEntity("ali", "alias1"));
                      list.Add(new BusinessEntity("reza", "alias2"));
                      return list;
                  }
              }

              کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: BlazorWasmReport.zip   
              نظرات مطالب
              آشنایی با Refactoring - قسمت 9
              برای اینکه احتمالا ASP.NET Webforms page life cycle رو رعایت نکردید و الان ViewState صفحه چیزی از وجود کنترل‌های پویای شما نمی‌دونه. مثلا می‌تونید از DynamicControlsPlaceholder استفاده کنید. اگر جزئیات بیشتری نیاز داشتید این مطالب مفید هستند:
              How To Perpetuate Dynamic Controls Between Page Views in ASP.NET
              Dynamic Web Controls, Postbacks, and View State
              Creating Dynamic Data Entry User Interfaces
              ASP.Net Dynamic Controls (Part 1)
              ASP.Net Dynamic Controls (Part 2)
              ASP.Net Dynamic Controls (Part 3)
              ASP.Net Dynamic Controls (Part 4)
              اشتراک‌ها
              بکارگیری Microsoft Report در Mvc

              In this post, I explain how to use Microsoft Report in MVC 4. Most of the developer use Microsoft Report (rdlc) for generating report  in asp.net application. Here I have explained how we can use Microsoft Report (rdlc) in MVC 

              بکارگیری Microsoft Report در Mvc
              مطالب
              استفاده یکپارچه از NUnit در VS.NET بدون نیاز به افزونه‌ها

              برای استفاده ساده‌تر از ابزارهای unit testing در ویژوال استودیو افزونه‌های زیادی وجود دارند، از ری شارپر تا CodeRush  تا حتی امکانات نسخه‌ی کامل VS.NET که با MSTest یکپارچه است. اما اگر نخواهیم از MSTest استفاده کنیم و همچنین افزونه‌ها را هم بخواهیم حذف کنیم (مثلا از نسخه‌ی رایگان express استفاده کنیم)، چطور؟
              برای حل این مشکل چندین روش وجود دارد. یا می‌شود از test runner این‌ها استفاده کرد که اصلا نیازی به IDE ندارند و مستقل است؛ یا می‌توان به صورت زیر هم عمل کرد:
              به خواص پروژه در VS.NET مراجعه کنید. برگه‌ی Build events را باز کنید. در اینجا می‌خواهیم post-build event را مقدار دهی کنیم. به این معنا که پس از هر build موفق، لطفا این دستورات خط فرمان را اجرا کن.
              NUnit به همراه test runner خط فرمان هم ارائه می‌شود و نام آن nunit-console.exe است. اگر به محل نصب آن مراجعه کنید، عموما در آدرس C:\Program Files\NUnit xyz\bin\nunit-console.exe قرار دارد. برای استفاده از آن تنها کافی است تنظیم زیر صورت گیرد:

              c:\path\nunit-console.exe /nologo $(TargetPath)

              TargetPath به صورت خودکار با نام اسمبلی جاری پروژه در زمان اجرا جایگزین می‌شود.
              اکنون پس از هر Build، به صورت خودکار nunit-console.exe اجرا شده، اسمبلی برنامه که حاوی آزمون‌های واحد است به آن ارسال گردیده و سپس خروجی کار در output window نمایش داده می‌شود. اگر خطایی هم رخ داده باشد در قسمت errors قابل مشاهده خواهد بود.
              در اینجا حتی بجای برنامه کنسول یاده شده می‌توان از برنامه nunit.exe هم استفاده کرد. در این حالت GUI اصلی پس از هر Build نمایش داده می‌شود:

              c:\path\nunit.exe $(TargetPath)


              چند نکته:
              1- برنامه nunit-console.exe چون در حال حاضر برای دات نت 2 کامپایل شده امکان بارگذاری dll های دات نت 4 را ندارد. به همین منظور فایل nunit-console.exe.config را باز کرده و تنظیمات زیر را به آن اعمال کنید:

              <configuration>  
              <startup>
              <supportedRuntime version="v4.0.30319" />
              </startup>

              و همچنین:

              <runtime>  
              <loadFromRemoteSources enabled="true" />

              2- خروجی نتایج اجرای آزمون‌ها را به صورت XML هم می‌توان ذخیره کرد. مثلا:

              c:\path\nunit-console.exe /xml:$(ProjectName)-tests.xml /nologo $(TargetPath)



              3- از فایل xml ذکر شده می‌توان گزارشات زیبایی تهیه کرد. برای مثال:
              Generating Report for NUnit
              NUnit2Report Task


              جهت مطالعه بیشتر:
              Setting up Visual C#2010 Express with NUnit
              Use Visual Studio's Post-Build Events to Automate Unit Testing Running
              3 Ways to Run NUnit From Visual Studio


              نظرات اشتراک‌ها
              StructureMap 3.0 منتشر شد
              سلام؛ اگه ممکنه در مورد نحوه تنظیم استراکچرمپ و web api راهنمایی کنید.