اگر به بانک اطلاعاتی مثالهای همراه سورسهای PdfReport در مسیر Bin\Data\blogs.sqlite مراجعه کنید، دو جدول والدین و فرزندان هم در آن وجود دارند:
بر این اساس قصد داریم رابطه یک به چند فوق را گروه بندی شده نمایش دهیم:
(البته این اعداد و اطلاعات، به صورت اتفاقی تولید شدهاند و الزامی ندارد که والد متولد 2002 هنوز والد شده باشد؛ یا اینکه فرزندی متولد 2003 داشته باشد!)
بنابراین صورت مساله ما به این ترتیب خواهد بود:
بر اساس اطلاعات دو جدول والدین و فرزندان فوق، اطلاعات نهایی را در جداول مجزایی بر اساس والدین و فرزندان آنها گروه بندی نمائید.
سورس کامل این مثال را در ادامه مشاهده میکنید:
به همراه سر ستونهای مجزای هر گروه و صفحه:
توضیحات:
- منبع داده مورد استفاده در اینجا از نوع GenericDataReader است؛ جهت خواندن رکوردهای بانک اطلاعاتی SQLite ذکر شده در ابتدای بحث. (دو مثال دیگر هم به پوشه مثالهای سورسهای PdfReport اضافه شدهاند به نامهای Grouping و WrapGroupsInColumns که به همین موضوع گروه بندی میپردازند؛ البته با استفاده از StronglyTypedListها. ولی درکل مفاهیم و اصول آنها یکی است.)
در کوئری فوق (و کلا گروه بندی اطلاعات) دو نکته حائز اهمیت است:
الف) چون قرار است اطلاعات بر اساس مشخصات والدین و فرزندان آنها گروه بندی شود، نیاز است حتما order by و مرتب سازی رکوردها قید گردد.
ب) در PdfReport نمیتوانید در خواص معرفی شده جهت تعریف ستونها، از نامهای تکراری استفاده کنید. برای رفع این مشکل استفاده از Alias پیشنهاد میشود؛ مانند:
- مشخص سازی خاصیت و ستونی که قرار است در گروه بندی شرکت کند بسیار ساده است:
در اینجا به کمک متد Group، قابلیت گروه بندی بر روی این ستون فعال شده و سپس باید فرمولی را جهت مشخص سازی حد و مرز گروه مشخص کنیم. برای مثال در اینجا اگر مقادیر ردیف جاری (val2) و ردیف قبلی (val1) یکسان نبودند، یعنی گروه خاتمه یافته و گروه جدیدی شروع میشود (به همین جهت عنوان شد که مرتب سازی اطلاعات ضروری است).
- تنظیم دیگری را که در اینجا میتوان ذکر کرد، مورد ذیل است:
به این ترتیب میتوان مشخص کرد که آیا باید ستونهای دخیل در گروه بندی، در گزارش نمایش داده شوند یا خیر (GroupType.HideGroupingColumns)، آیا سر ستون هر جدول، به ازای هر گروه باید تکرار شود؟ (RepeatHeaderRowPerGroup)، آیا در هر صفحه یک گروه نمایش داده شود (ShowOneGroupPerPage) یا اینکه گروهها به صورت متوالی در صفحات درج شوند. توسط SpacingBeforeAllGroupsSummary، فاصله جمع نهایی تمام گروهها از آخرین گروه نمایش داده شده مشخص میشود. به کمک NewGroupAvailableSpacingThreshold مشخص میکنیم که در چه فاصلهای از انتهای صفحه، گروه جدیدی نباید درج شود و این گروه باید به صفحه بعدی منتقل شده و از آنجا شروع شود.
- اگر به تصویر ابتدای مطلب دقت کرده باشید، علاوه بر هدر صفحه، هر گروه نیز یک هدر مجزا دارد. برای طراحی آن باید اینترفیس IPageHeader را پیاده سازی کرد که نمونهای از آنرا در کلاس MasterDetailsHeaders فوق مشاهده میکنید.
ساختار آن هم بسیار ساده است. توسط newGroupInfo میتوان به اطلاعات گروه جدید، دسترسی یافت. برای مثال در اینجا اطلاعات والد گروه جدید در حال تهیه، دریافت شده و سپس در ردیفهای یک جدول دو ستونه درج میشود. در ستون اول آن یک برچسب و در ستون دوم، مقدار دریافتی نمایش داده شده است و همینطور الی آخر برای سایر ردیفها.
بر این اساس قصد داریم رابطه یک به چند فوق را گروه بندی شده نمایش دهیم:
(البته این اعداد و اطلاعات، به صورت اتفاقی تولید شدهاند و الزامی ندارد که والد متولد 2002 هنوز والد شده باشد؛ یا اینکه فرزندی متولد 2003 داشته باشد!)
بنابراین صورت مساله ما به این ترتیب خواهد بود:
بر اساس اطلاعات دو جدول والدین و فرزندان فوق، اطلاعات نهایی را در جداول مجزایی بر اساس والدین و فرزندان آنها گروه بندی نمائید.
سورس کامل این مثال را در ادامه مشاهده میکنید:
using System; using PdfRpt.Core.Contracts; using PdfRpt.FluentInterface; namespace PdfReportSamples.MasterDetails { public class MasterDetailsPdfReport { public IPdfReportData CreatePdfReport() { return new PdfReport().DocumentPreferences(doc => { doc.RunDirection(PdfRunDirection.LeftToRight); 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(Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\arial.ttf", Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf"); }) .PagesHeader(header => { header.CustomHeader(new MasterDetailsHeaders { PdfRptFont = header.PdfFont }); }) .PagesFooter(footer => { footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy")); }) .MainTableTemplate(t => t.BasicTemplate(BasicTemplate.SilverTemplate)) .MainTablePreferences(table => { table.ColumnsWidthsType(TableColumnWidthType.Relative); table.GroupsPreferences(new GroupsPreferences { GroupType = GroupType.HideGroupingColumns, RepeatHeaderRowPerGroup = true, ShowOneGroupPerPage = false, SpacingBeforeAllGroupsSummary = 5f, NewGroupAvailableSpacingThreshold = 170 }); }) .MainTableDataSource(dataSource => { dataSource.GenericDataReader( providerName: "System.Data.SQLite", connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite", sql: @"select tblParents.BirthDate as ParentBirthDate, tblParents.Name as ParentName, tblParents.LastName as ParentLastName, tblKids.Name as KidName, tblKids.BirthDate as KidBirthDate from tblParents left outer join tblKids on tblKids.ParentId = tblParents.Id order by tblParents.Name, tblParents.LastName, tblKids.Name" ); }) .MainTableColumns(columns => { columns.AddColumn(column => { column.PropertyName("rowNo"); column.IsRowNumber(true); column.CellsHorizontalAlignment(HorizontalAlignment.Left); column.IsVisible(true); column.Order(0); column.Width(1); column.HeaderCell("#"); }); columns.AddColumn(column => { column.PropertyName("ParentBirthDate"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.Order(1); column.Width(2); column.HeaderCell("ParentBirthDate"); column.Group(true, (val1, val2) => { var date1 = (DateTime)val1; var date2 = (DateTime)val2; return date1.Year == date2.Year && date1.Month == date2.Month && date1.Day == date2.Day; }); }); columns.AddColumn(column => { column.PropertyName("ParentName"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.Order(2); column.Width(2); column.HeaderCell("ParentName"); column.Group(true, (val1, val2) => { return val1.ToString() == val2.ToString(); }); }); columns.AddColumn(column => { column.PropertyName("ParentLastName"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.Order(3); column.Width(2); column.HeaderCell("ParentLastName"); column.Group(true, (val1, val2) => { return val1.ToString() == val2.ToString(); }); }); columns.AddColumn(column => { column.PropertyName("KidName"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.Order(4); column.Width(2); column.HeaderCell("Child Name"); column.IsVisible(true); }); columns.AddColumn(column => { column.PropertyName("KidBirthDate"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.Order(5); column.Width(2); column.HeaderCell("BirthDate"); column.IsVisible(true); }); }) .MainTableEvents(events => { events.DataSourceIsEmpty(message: "There is no data available to display."); }) .Export(e => e.ToExcel()) .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptMasterDetailsSample.pdf")); } } }
using System.Collections.Generic; using iTextSharp.text; using iTextSharp.text.pdf; using PdfRpt.ColumnsItemsTemplates; using PdfRpt.Core.Contracts; using PdfRpt.Core.Helper; namespace PdfReportSamples.MasterDetails { public class MasterDetailsHeaders : IPageHeader { public IPdfFont PdfRptFont { set; get; } public PdfPTable RenderingGroupHeader(Document pdfDoc, PdfWriter pdfWriter, IList<CellData> newGroupInfo, IList<SummaryCellData> summaryData) { var parentName = newGroupInfo.GetSafeStringValueOf("ParentName"); var parentLastName = newGroupInfo.GetSafeStringValueOf("ParentLastName"); var parentBirthDate = newGroupInfo.GetSafeStringValueOf("ParentBirthDate"); var table = new PdfPTable(relativeWidths: new[] { 1f, 5f }) { WidthPercentage = 100 }; table.AddSimpleRow( (cellData, cellProperties) => { cellData.Value = "Name:"; cellProperties.PdfFont = PdfRptFont; cellProperties.PdfFontStyle = DocumentFontStyle.Bold; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }, (cellData, cellProperties) => { cellData.Value = parentName; cellProperties.PdfFont = PdfRptFont; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }); table.AddSimpleRow( (cellData, cellProperties) => { cellData.Value = "Last Name:"; cellProperties.PdfFont = PdfRptFont; cellProperties.PdfFontStyle = DocumentFontStyle.Bold; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }, (cellData, cellProperties) => { cellData.Value = parentLastName; cellProperties.PdfFont = PdfRptFont; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }); table.AddSimpleRow( (cellData, cellProperties) => { cellData.Value = "Birth Date:"; cellProperties.PdfFont = PdfRptFont; cellProperties.PdfFontStyle = DocumentFontStyle.Bold; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }, (cellData, cellProperties) => { cellData.Value = parentBirthDate; cellProperties.PdfFont = PdfRptFont; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }); return table.AddBorderToTable(borderColor: BaseColor.LIGHT_GRAY, spacingBefore: 5f); } public PdfPTable RenderingReportHeader(Document pdfDoc, PdfWriter pdfWriter, IList<SummaryCellData> summaryData) { var table = new PdfPTable(numColumns: 1) { WidthPercentage = 100 }; table.AddSimpleRow( (cellData, cellProperties) => { cellData.CellTemplate = new ImageFilePathField(); cellData.Value = AppPath.ApplicationPath + "\\Images\\01.png"; cellProperties.HorizontalAlignment = HorizontalAlignment.Center; }); table.AddSimpleRow( (cellData, cellProperties) => { cellData.Value = "Family rpt"; cellProperties.PdfFont = PdfRptFont; cellProperties.PdfFontStyle = DocumentFontStyle.Bold; cellProperties.HorizontalAlignment = HorizontalAlignment.Center; }); return table.AddBorderToTable(); } } }
- منبع داده مورد استفاده در اینجا از نوع GenericDataReader است؛ جهت خواندن رکوردهای بانک اطلاعاتی SQLite ذکر شده در ابتدای بحث. (دو مثال دیگر هم به پوشه مثالهای سورسهای PdfReport اضافه شدهاند به نامهای Grouping و WrapGroupsInColumns که به همین موضوع گروه بندی میپردازند؛ البته با استفاده از StronglyTypedListها. ولی درکل مفاهیم و اصول آنها یکی است.)
select tblParents.BirthDate as ParentBirthDate, tblParents.Name as ParentName, tblParents.LastName as ParentLastName, tblKids.Name as KidName, tblKids.BirthDate as KidBirthDate from tblParents left outer join tblKids on tblKids.ParentId = tblParents.Id order by tblParents.Name, tblParents.LastName, tblKids.Name
الف) چون قرار است اطلاعات بر اساس مشخصات والدین و فرزندان آنها گروه بندی شود، نیاز است حتما order by و مرتب سازی رکوردها قید گردد.
ب) در PdfReport نمیتوانید در خواص معرفی شده جهت تعریف ستونها، از نامهای تکراری استفاده کنید. برای رفع این مشکل استفاده از Alias پیشنهاد میشود؛ مانند:
tblParents.Name as ParentName, tblKids.Name as KidName,
column.Group(true, (val1, val2) => { return val1.ToString() == val2.ToString(); });
- تنظیم دیگری را که در اینجا میتوان ذکر کرد، مورد ذیل است:
table.GroupsPreferences(new GroupsPreferences { GroupType = GroupType.HideGroupingColumns, RepeatHeaderRowPerGroup = true, ShowOneGroupPerPage = false, SpacingBeforeAllGroupsSummary = 5f, NewGroupAvailableSpacingThreshold = 170 });
- اگر به تصویر ابتدای مطلب دقت کرده باشید، علاوه بر هدر صفحه، هر گروه نیز یک هدر مجزا دارد. برای طراحی آن باید اینترفیس IPageHeader را پیاده سازی کرد که نمونهای از آنرا در کلاس MasterDetailsHeaders فوق مشاهده میکنید.
public PdfPTable RenderingGroupHeader(Document pdfDoc, PdfWriter pdfWriter, IList<CellData> newGroupInfo, IList<SummaryCellData> summaryData) { var parentName = newGroupInfo.GetSafeStringValueOf("ParentName"); var parentLastName = newGroupInfo.GetSafeStringValueOf("ParentLastName"); var parentBirthDate = newGroupInfo.GetSafeStringValueOf("ParentBirthDate"); var table = new PdfPTable(relativeWidths: new[] { 1f, 5f }) { WidthPercentage = 100 }; table.AddSimpleRow( (cellData, cellProperties) => { cellData.Value = "Name:"; cellProperties.PdfFont = PdfRptFont; cellProperties.PdfFontStyle = DocumentFontStyle.Bold; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; }, (cellData, cellProperties) => { cellData.Value = parentName; cellProperties.PdfFont = PdfRptFont; cellProperties.HorizontalAlignment = HorizontalAlignment.Left; });