C#에서 PrintDocument를 사용하여 간단한 보고서를 만드는 방법 > 블로그 & Tips

본문 바로가기

ComponentOne

블로그 & Tips

C#에서 PrintDocument를 사용하여 간단한 보고서를 만드는 방법

페이지 정보

작성자 GrapeCity 작성일 21-12-01 15:32 조회 2,239회 댓글 0건

본문

보고는 비즈니스 분야에서 일반적인 작업입니다. 복잡한 보고서를 만들 수 있는 특수 FlexReport 컨트롤이 있지만 특수 도구를 사용하는 것은 너무 어렵거나 유연하지 않을 수 있습니다. 이러한 경우를 위해 더욱 간단하고 가벼운 도구인 C1PrintDocument가 있습니다.

수년 동안 C1PrintDocument 컴포넌트는 C1Report 어셈블리에 포함되었지만 사용이 중단되었습니다. 2021 v2 릴리스에서는 C1PrintDocument를 어셈블리로 분리하는 동시에 .NET 5에 이식했습니다.

C1PrintDocument 및 관련 미리 보기 컨트롤은 C1.Win.Printing NuGet 패키지(WinForms 버전)에 새로 추가되었습니다. 이제 WPF 버전은 .NET 4.5.2에서만 사용할 수 있으며, C1.Xaml.WPF.PrintDocument에서 볼 수 있습니다.


C1PrintDocument 소개

C1PrintDocument는 다양한 레이아웃(인라인, 스택), 주변 및 계층 스타일, 무한대로 중첩된 표, 목차, 페이지 매김 정밀 컨트롤(단락의 첫 줄이나 마지막 줄 분리 방지, 페이지 크기/헤더/푸터 등) 등에 대한 지원과 함께 다양한 기능을 갖춘 광범위한 DOM(문서 객체 모델)을 제공합니다. 무엇보다 데이터 바인딩 레이어도 있습니다.

이러한 내용에 대해 잘 알고 있으면 코드를 사용하여 복잡하고 유연한 문서를 비교적 쉽게 만든 다음 PDF(Portable Document Format), RTF(서식 있는 텍스트 파일), MS Excel 및 기타 형식으로 보거나 인쇄하거나 저장하거나 내보낼 수 있습니다.

페이지 매김 및 여러 레이아웃 옵션을 사용하여 임의 텍스트를 인쇄해야 하는 경우 다음과 같은 간단한 코드를 사용하여 시작할 수 있습니다.


C1PrintDocument을 사용하여 "Hello World" 출력하기

// create C1PrintDocument instance
var doc = new C1.C1Preview.C1PrintDocument();
​
// insert content
doc.Body.Children.Add(new C1.C1Preview.RenderText(doc, "Hello World!"));
​
//force document generation
doc.Generate();
​
// preview what you got with RibbonPreviewDialog
var preview = new C1.Win.RibbonPreview.C1RibbonPreviewDialog();
preview.Document = doc;
preview.ShowDialog();
 

미리 보기 대화 상자에서 페이지 설정을 사용하여 콘텐츠 공간이 조정되는 방식을 보고 코드 작업 없이 페이지를 매길 수 있습니다.

보다 복잡한 보고서와 같은 응용 프로그램이 필요한 경우 요소의 레이아웃을 정의하고 데이터 바인딩을 사용하여 데이터에 따라 요소를 생성해야 합니다. 작업 방법을 보여 주기 위해 압축 파일로 다운로드할 수 있는 SimpleReports 샘플을 제공해 드립니다. 이제 몇 가지 개별 샘플과 기술에 대해 말씀드리겠습니다.


간단한 보고서 만들기

가장 단순한 "고객 레이블" 샘플에는 왼쪽에서 오른쪽 방향의 레이블을 한 페이지에 들어갈 수 있는 한 최대한 많이 표시합니다.

C1PrintDocument를 작성하려면 RenderObject 클래스에서 파생된 요소를 사용하고 C1PrintDocument.Body에 추가해야 합니다.

WinForms 응용 프로그램에서 control.Controls 컬렉션에 컨트롤을 추가한 것과 동일한 방식으로 Children(자식) 컬렉션에 추가합니다. 요소를 중첩할 수 있고 부모 객체 바인딩을 데이터 소스로 사용할 수 있습니다.

RenderArea는 자식 객체를 위한 컨테이너인 렌더 객체입니다. 기본적으로 새 RenderArea가 생성되면 그 너비는 부모 영역의 너비와 같습니다.

RenderArea의 경우 너비 또는 높이를 "Unit.Auto"로 지정하면 자식의 크기가 적절한 크기를 결정합니다.

컨테이너(부모 객체 또는 문서 본문) 내에서 기본적으로 렌더 객체는 누적 규칙에 따라 배치되고 컨테이너(최상위 객체 또는 문서 바디)의 Stacking 속성 값으로 결정됩니다.

이 값은 다음 StackingRulesEnum 열거 멤버 중 하나일 수 있습니다.

  • BlockTopToBottom: 객체는 컨테이너 내에서 다른 객체 아래에 배치됩니다. 현재 페이지의 아래쪽 가장자리에 도달하면 새 페이지가 추가됩니다. 이는 기본값입니다.

  • BlockLeftToRight: 객체가 왼쪽에서 오른쪽으로 다른 객체 옆에 배치됩니다. 현재 페이지의 오른쪽 가장자리에 도달하면 새 "가로" 페이지가 추가됩니다. 가로 페이지는 이전 페이지를 논리적으로 오른쪽으로 확장합니다.

  • InlineLeftToRight: 객체가 왼쪽에서 오른쪽으로 다른 객체 옆에 인라인 방식으로 배치됩니다. 현재 페이지의 오른쪽 가장자리에 도달하면 시퀀스가 다음 줄로 줄바꿈됩니다. 현재 페이지의 맨 아래에 도달하면 새 페이지가 추가됩니다.

"고객 레이블" 문서의 레이아웃은 다음과 같은 여러 가지 핵심 요소로 구성됩니다.

  1. 외부 RenderArea 객체 "raContainer"는 모든 레이블에 대한 컨테이너로 작동합니다.

  2. RenderArea 객체 "raItem"은 단일 레이블을 나타냅니다.

  3. RenderText 객체 "rt"는 레이블 콘텐츠를 나타냅니다.

다음과 같이 나타납니다.

고객 레이블


코드 중 일부분을 좀 더 자세히 보여 드리겠습니다.


고객 레이블 코드

// create outer render area
RenderArea raContainer = new RenderArea();
​
// define left-to-right flow for child elements
raContainer.Stacking = StackingRulesEnum.InlineLeftToRight;
​
_printDocument.Body.Children.Add(raContainer);
​
// define data schema
DataSource ds = CreateDemoDataSource();
DataSet dsCustomers = new DataSet(ds, "SELECT CompanyName, Address, City, PostalCode, Country FROM Customers ORDER BY CompanyName");
​
// add data set to the document
_printDocument.DataSchema.DataSets.Add(dsCustomers);
​
// create render area representing single label
RenderArea raItem = new RenderArea();
​
// set right and bottom borders as light gray dotted lines 0.1 point wide
raItem.Style.Borders.Right = new LineDef("0.1pt", Color.LightGray, System.Drawing.Drawing2D.DashStyle.Dot);
raItem.Style.Borders.Bottom = new LineDef("0.1pt", Color.LightGray, System.Drawing.Drawing2D.DashStyle.Dot);
​
// set size in millimeters
raItem.Width = "40mm";
raItem.Height = "20mm";
​
// do not split the label into different pages
raItem.SplitVertBehavior = SplitBehaviorEnum.Never;
​
// set the data source
raItem.DataBinding.DataSource = dsCustomers;
​
// add label as a child to outer container
raContainer.Children.Add(raItem);
​
// define text which should be printed inside label
RenderText rt = new RenderText();
rt.Text = "[Fields!CompanyName.Value]\r\n[Fields!Address.Value]\r\n[Fields!City.Value] [Fields!PostalCode.Value]\r\n[Fields!Country.Value]";
​
// Add the text as a child of label
raItem.Children.Add(rt);
​
// generate document
_printDocument.Generate();
 
각 레이블의 위 코드 텍스트는 스크립팅 언어를 사용하는 데이터 바인딩 필드로 구성되어 있습니다. 문서 생성 중 C1PrintDocument는 이러한 필드를 계산하여 실제 값을 인쇄합니다. 텍스트 식에 대한 자세한 내용은 여기에서 확인할 수 있습니다. 


그룹 사용

C1PrintDocument를 사용하면 식을 사용하여 데이터를 그룹화할 수 있습니다. "제품의 사전순 목록" 샘플은 제품 이름의 첫 번째 문자로 제품을 그룹화하여 아래와 같이 보기 좋게 정렬된 제품 목록을 보여 줍니다.

제품의 사전순 목록


이 샘플에는 데이터가 표 형식으로 배치되어 있습니다. 이 표는 RenderTable 클래스를 사용하여 만들었습니다. 표의 크기에는 제한이 없고, 콘텐츠가 지정된 가장 높은 행 및 열 번호에 해당하는 셀에 따라 렌더링 시 표 크기가 결정됩니다.

행과 열의 인덱스는 0부터 시작합니다. 기본적으로 표의 크기는 부모 요소의 클라이언트 영역 너비와 동일합니다. 행 높이는 행에서 가장 큰 콘텐츠 높이에 맞춰 자동으로 설정됩니다.


제품의 사전순 목록 코드

// Define data schema
DataSource ds = CreateDemoDataSource();
​
// Create DataSet containing data selected from database
// "FirstLetter" field is the first letter of product name
DataSet dsProducts = new DataSet(ds,
      "SELECT Left(p.ProductName, 1) AS FirstLetter, p.ProductName, p.QuantityPerUnit, p.UnitsInStock, c.CategoryName " +
      "FROM Categories c, Products p " +
      "WHERE c.CategoryID = p.CategoryID " +
      "ORDER BY p.ProductName");
​
// Add data source and data set to the document:
// this will preserve the data binding if the document is saved as c1d/c1dx
_printDocument.DataSchema.DataSources.Add(ds);
_printDocument.DataSchema.DataSets.Add(dsProducts);
​
// RenderTable class is used to create a table
RenderTable rt = new RenderTable();
​
// TableVectorGroup collection is used to group the data by "FirstLetter" field.
// The RowGroups property accepts two integers.
// The first value is the index of the first row included in the group (0).
// The second value is the count of rows in the group (3).
TableVectorGroup tvg = rt.RowGroups[0, 3];
​
// Using the DataBinding property in the TableVectorGroup, which is the base class for table row and column groups, a RenderTable can be data bound.
tvg.DataBinding.DataSource = dsProducts;
​
// Group by “FirstLetter” query result field
tvg.DataBinding.Grouping.Expressions.Add("Fields!FirstLetter.Value");
​
// Set header row
rt.Cells[0, 0].Text = "[Fields!FirstLetter.Value]";
​
// Merge cells from [0, 1] to [0, 4]
rt.Cells[0, 1].SpanCols = 4;
​
// Set sub-header row
rt.Cells[1, 1].Text = "Product Name:";
rt.Cells[1, 2].Text = "Category Name:";
rt.Cells[1, 3].Text = "Quantity Per Unit:";
rt.Cells[1, 4].Text = "Units In Stock:";
​
// Set data row
// "[Fields!ProductName.Value]" means to take the value of the "ProductName" field from the query result
rt.Cells[2, 1].Text = "[Fields!ProductName.Value]";
rt.Cells[2, 2].Text = "[Fields!CategoryName.Value]";
rt.Cells[2, 3].Text = "[Fields!QuantityPerUnit.Value]";
rt.Cells[2, 4].Text = "[Fields!UnitsInStock.Value]";
​
// Create a nested (second) group of rows
tvg = rt.RowGroups[2, 1];
tvg.DataBinding.DataSource = dsProducts;
​
// Add table to the document
_printDocument.Body.Children.Add(rt);
​
// Generate document
_printDocument.Generate();
 

참고: 그룹의 범위는 겹치면 안 됩니다.



표현식 사용하기

표현식(또는 스크립트)은 C1PrintDocument에서 콘텐츠를 추출, 계산, 표시, 그룹화, 정렬, 필터링, 매개 변수화하고, 콘텐츠의 서식을 지정하며, 보고서의 기능을 확장하는 데 사용됩니다. 스크립트에 대한 자세한 내용은 여기에서 확인할 수 있습니다.

기본적으로 Visual Basic이 표현식 언어로 사용됩니다. C#을 사용하려면 ScriptingOptions.Language 속성을 변경하세요.

"Employees" 샘플에서는 스크립트를 사용하여 생성된 문서에 사진을 삽입합니다.

직원


이 샘플에서는 FormatDataBindingInstanceScript 속성을 사용하여 데이터 바인딩 확인으로 인해 현재 RenderObject의 새 인스턴스가 생성될 때마다 실행되는 스크립트를 설정합니다.


Employees 코드

// define data set
DataSet dsEmployers = new DataSet(ds,
     "SELECT EmployeeID, LastName, FirstName, Title, TitleOfCourtesy, BirthDate, HireDate, Address, City, Region, PostalCode, Country, HomePhone, Extension, Notes, ReportsTo, Photo " +
     "FROM Employees " +
     "ORDER BY Country, City, FirstName, LastName");

// add data source and data set to the document: this will preserve the data binding if the document is saved as c1d/c1dx
_printDocument.DataSchema.DataSources.Add(ds);
_printDocument.DataSchema.DataSets.Add(dsEmployers);

// create table
RenderTable rt = new RenderTable();

// set header row
rt.Cells[0, 0].Text = "Country";
rt.Cells[0, 1].Text = "City";
rt.Cells[0, 2].Text = "Address";
rt.Cells[0, 3].Text = "Home Phone";

// set country row
rt.Cells[1, 0].Text = "[Fields!Country.Value]";

// set city row
rt.Cells[2, 1].Text = "[Fields!City.Value]";

// set data rows
rt.Cells[3, 0].Text = "[Fields!FirstName.Value] [Fields!LastName.Value]";
rt.Cells[3, 0].SpanCols = 2;

rt.Cells[3, 2].Text = "[Fields!Address.Value]";
rt.Cells[3, 3].Text = "[Fields!HomePhone.Value]";

RenderImage ri = new RenderImage(_printDocument);

// show all exceptions and warnings for script debug
_printDocument.ThrowExceptionOnError = true;
_printDocument.AddWarningsWhenErrorInScript = true;

// using the VB script to extract an image from query resault
ri.FormatDataBindingInstanceScript = @"
     ' get ri object
     Dim ri as RenderImage = DirectCast(RenderObject, RenderImage)

     ' get DB BLOB object as byte array
     Dim picData as Byte() = DirectCast(RenderObject.Original.DataBinding.Parent.Fields!Photo.Value, Byte())
     Const bmData As Integer = 78
     Dim ms as IO.MemoryStream = New IO.MemoryStream(picData, bmData, picData.Length - bmData)

     ' create image from stream
     ri.Image = Image.FromStream(ms)
     ";

// set image parameters
ri.Width = "30mm";
ri.Height = "30mm";
ri.Style.ImageAlign.AlignHorz = ImageAlignHorzEnum.Center;
ri.Style.ImageAlign.AlignVert = ImageAlignVertEnum.Center;

rt.Cells[4, 0].RenderObject = ri;
rt.Cells[4, 0].SpanCols = 2;
rt.Cells[4, 0].SpanRows = 2;

rt.Cells[4, 2].Text = "[Fields!Title.Value]";
rt.Cells[4, 2].Style.Parents = dataStyle;

rt.Cells[4, 3].Text = "[FormatDateTime(Fields!BirthDate.Value, DateFormat.ShortDate)]    [FormatDateTime(Fields!HireDate.Value, DateFormat.ShortDate)]";
rt.Cells[4, 3].Style.Parents = dataStyle;

// add area for notes
RenderArea raNotes = new RenderArea();

var rtNoteTitle = new RenderText();
rtNoteTitle.Text = "[Fields!FirstName.Value]`s notes:";

// add note text
var rtNote = new RenderText();
rtNote.Text = "[Fields!Notes.Value]";

// do not split the area into different pages
raNotes.SplitVertBehavior = SplitBehaviorEnum.Never;

raNotes.Children.Add(rtNoteTitle);
raNotes.Children.Add(rtNote);

rt.Cells[5, 2].RenderObject = raNotes;

// create group by city
TableVectorGroup tvg = rt.RowGroups[2, 6];
tvg.DataBinding.DataSource = dsEmployers;
tvg.DataBinding.Grouping.Expressions.Add("Fields!City.Value");

// create group by country
tvg = rt.RowGroups[0, 8];
tvg.DataBinding.DataSource = dsEmployers;
tvg.DataBinding.Grouping.Expressions.Add("Fields!Country.Value");

// add data rows
tvg = rt.RowGroups[3, 5];
tvg.DataBinding.DataSource = dsEmployers;
tvg.SplitBehavior = SplitBehaviorEnum.Never;

// add table to the document
_printDocument.Body.Children.Add(rt);

// define a cell group style for the rectangular area of table cells, you can set the style rt.UserCellGroups.Add(new UserCellGroup(new Rectangle(0, 3, 4, 3)));
rt.UserCellGroups[0].Style.Borders.All = new LineDef("0.5pt", Color.Black);

// generate document
_printDocument.Generate();


C1PrintDocument 계층 구조의 여러 객체에는 데이터에 따라 즉시 모양 변화를 허용하는 스크립트를 수락하는 속성이 있습니다. 예를 들어, .TextColorExpr 식에서는 스타일을 사용하여 $1000가 넘는 주문을 파란색으로 강조 표시할 수 있습니다.


조건별 텍스트 색

rt.Cells[3, 4].Style.TextColorExpr = "iif(Fields!UnitPrice.Value * Fields!Quantity.Value >= 1000, Colors.Blue, Colors.Black)";



스타일 사용

C1PrintDocument Style 속성은 테두리, 글꼴, 텍스트의 선 간격 등 시각적 컴포넌트의 기본 모양을 설정할 수 있는 문서의 루트 스타일입니다.

C1PrintDocument PageLayout.PageSettings 속성을 사용하면 인쇄를 위한 페이지 옵션을 설정하여 용지 크기, 페이지 방향 등을 선택할 수 있습니다.

문서 페이지의 경우 RenderObject 클래스의 Style 속성을 사용하여 설정을 조정할 수 있습니다. 이 속성은 할당할 수 없습니다. 현재 객체의 스타일에 대해 다른 스타일을 기본으로 지정하려면 Parent를 다른 스타일로 설정하세요.


스타일 설정

// set default style for document
_printDocument.Style.FontName = "Verdana";
_printDocument.Style.FontSize = 10;
​
// set margin to pages in millimeters
_printDocument.PageLayout.PageSettings.LeftMargin = "12mm";
_printDocument.PageLayout.PageSettings.RightMargin = "12mm";
_printDocument.PageLayout.PageSettings.TopMargin = "12mm";
_printDocument.PageLayout.PageSettings.BottomMargin = "12mm";
​
// set group header style
// for large documents it is better to create a named style for reuse
C1.C1Preview.Style headerStyle = _printDocument.Style.Children.Add();
headerStyle.FontSize = 9;
headerStyle.FontBold = true;
headerStyle.GridLines.Bottom = new LineDef("1pt", Color.Black);
​
// set country header style
C1.C1Preview.Style countryStyle = _printDocument.Style.Children.Add();
countryStyle.FontSize = 11;
countryStyle.FontBold = true;
​
// set city header style
C1.C1Preview.Style cityStyle = _printDocument.Style.Children.Add();
cityStyle.FontSize = 10;
cityStyle.FontUnderline = true;
​
// set document caption
var rtCaption = new RenderText();
rtCaption.Text = "Employees";
​
// set style parameters directly is fine for small documents or styles used just once
rtCaption.Style.FontName = "Tahoma";
rtCaption.Style.FontSize = 16;
rtCaption.Style.Padding.All = "2mm";
rtCaption.Style.BackColor = Color.LightGray;
​
_printDocument.Body.Children.Add(rtCaption);
​
// create table
RenderTable rt = new RenderTable();
​
// set top, left, bottom, right padding of content within the cell to 1 millimeter
rt.CellStyle.Padding.All = "1mm";
​
// set header row
rt.Cells[0, 0].Text = "Country";
rt.Cells[0, 0].Style.Spacing.Top = "2mm";
rt.Cells[0, 0].Style.Parents = headerStyle;
​
rt.Cells[0, 1].Text = "City";
rt.Cells[0, 1].Style.Parents = headerStyle;
​
rt.Cells[0, 2].Text = "Address";
rt.Cells[0, 2].Style.Parents = headerStyle;
​
rt.Cells[0, 3].Text = "Home Phone";
rt.Cells[0, 3].Style.Parents = headerStyle;
​
// set country row
rt.Cells[1, 0].Text = "[Fields!Country.Value]";
rt.Cells[1, 0].Style.Parents = countryStyle;
​
// set city row
rt.Cells[2, 1].Text = "[Fields!City.Value]";
rt.Cells[2, 1].Style.Parents = cityStyle;
​
// define style for the rectangular area of table cells from cell where row = 0, column = 3, the 4 cells width and the 3 cells height
rt.UserCellGroups.Add(new UserCellGroup(new Rectangle(0, 3, 4, 3)));
​
// add to cell the black border of 0.5 point width
rt.UserCellGroups[0].Style.Borders.All = new LineDef("0.5pt", Color.Black);



집계 사용

평균 보고서의 경우 단순히 데이터를 그룹화하는 것으로는 충분치 않습니다. 사람들은 일반적으로 보고서 맨 아래에서 집계된 값을 보고 싶어 합니다. "국가별 직원 매출" 샘플에서 집계를 추가하는 방법을 살펴보겠습니다.

국가별 직원 매출


국가별 직원 매출 코드

// set dates
var date1 = new DateTime(2015, 9, 7, 0, 0, 0);
var date2 = new DateTime(2016, 5, 5, 0, 0, 0);
​
var dsSales = new DataSet(dataSource, string.Format(
    "SELECT o.ShipCountry, e.EmployeeID, e.FirstName, e.LastName, o.ShippedDate, o.Freight " +
    "FROM Employees e, Orders o " +
    "WHERE e.EmployeeID = o.EmployeeID AND o.ShippedDate IS NOT NULL AND o.ShippedDate BETWEEN #{0} 00:00:00# AND #{1} 00:00:00# " +
    "ORDER BY o.ShipCountry, e.FirstName, e.LastName, o.ShippedDate", date1.ToString(@"MM\/dd\/yyyy"), date2.ToString(@"MM\/dd\/yyyy")));
​
// add data source and data set to the document: this will preserve the data binding if the document is saved as c1d/c1dx
_printDocument.DataSchema.DataSources.Add(dataSource);
_printDocument.DataSchema.DataSets.Add(dsSales);
​
// add caption
var raCaption = new RenderArea();
​
var header1 = new RenderText();
header1.Text = "Employee sales by country";
​
var header2 = new RenderText();
header2.Text = string.Format("Between {0} and {1}", date1.ToShortDateString(), date2.ToShortDateString());
​
raCaption.Children.Add(header1);
raCaption.Children.Add(header2);
​
_printDocument.Body.Children.Add(raCaption);
​
// create table
var rt = new RenderTable();
​
// header: country
rt.Cells[0, 0].Text = "[Fields!ShipCountry.Value]";
rt.Cells[0, 2].Text = "$[Aggregates!SumByCountry.Value]";
​
// data
rt.Cells[1, 0].Text = "[Fields!FirstName.Value] [Fields!LastName.Value]";
rt.Cells[1, 1].Text = "[Math.Round(Aggregates!SumByEmployee.Value / Aggregates!SumByCountry.Value * 100, 1)]%";
rt.Cells[1, 2].Text = "$[Aggregates!SumByEmployee.Value]";
​
// create a group by ship country to be repeated for each group
TableVectorGroup tvg = rt.RowGroups[0, 2];
tvg.DataBinding.DataSource = dsSales;
​
// data is grouped by the "ShipCountry" field
tvg.DataBinding.Grouping.Expressions.Add("Fields!ShipCountry.Value");
​
// Create an aggregate that will calculate the sum of "Freight" fields within each grouping by the "ShipCountry" field
_printDocument.DataSchema.Aggregates.Add(new Aggregate("SumByCountry", "Fields!Freight.Value", tvg.DataBinding, RunningEnum.Group, AggregateFuncEnum.Sum));
​
tvg = rt.RowGroups[1, 1];
tvg.DataBinding.DataSource = dsSales;
tvg.DataBinding.Grouping.Expressions.Add("Fields!EmployeeID.Value");
​
// add total sum by employee aggregate value: data grouped by the "EmployeeID" field, the sum of the "Freight" field values is calculated
_printDocument.DataSchema.Aggregates.Add(new Aggregate("SumByEmployee", "Fields!Freight.Value", tvg.DataBinding, RunningEnum.Group, AggregateFuncEnum.Sum));
​
// add data rows
tvg = rt.RowGroups[1, 1];
tvg.DataBinding.DataSource = dsSales;
​
// add table to the document
_printDocument.Body.Children.Add(rt);
​
// generate document
_printDocument.Generate();


위 코드에서 집계는 메서드 호출 new Aggregate("SumByCountry", "Fields!Freight.Value", tvg.DataBinding, RunningEnum.Group, AggregateFuncEnum.Sum)으로 생성됩니다. 매개 변수는 다음과 같습니다.

  1. "SumByCountry"는 집계 이름입니다.

  2. "Fields!Freight.Value"는 합계를 계산하기 위한 식입니다.

  3. "tvg.DataBinding"은 집계를 위한 데이터 소스입니다.

  4. "RunningEnum.Group"은 집계에 그룹 범위가 있음을 의미합니다.

  5. "AggregateFuncEnum.Sum"은 집계가 범위 내에서 식의 값 합계를 반환함을 의미합니다.

C1PrintDocument.DataSchema.Aggregates.Add 메서드는 Aggregates 컬렉션에 Aggregate 객체를 추가합니다. 그러면 이름이 "SumByCountry"인 집계를 문서의 모든 위치에서 사용할 수 있습니다(예: rt.Cells[0, 2].Text = "$[Aggregates!SumByCountry.Value]").

document.Aggregates 컬렉션에 집계를 반드시 추가할 필요는 없습니다. 여기에 표시된 대로 집계 함수는 모든 식에서 직접 사용할 수 있습니다.


차트 그리기

대부분 사람들은 데이터를 시각적으로 표현한 것을 보고 싶어 합니다. "카테고리 별 매출" 샘플에서는 FlexChart 컨트롤을 사용하여 차트를 생성한 다음 문서에 이미지를 삽입합니다.

다음은 이 샘플의 미리 보기입니다.

범주별 매출


C1PrintDocument 스크립트에서 임의의 어셈블리를 사용하려면 C1PrintDocument.ScriptingOptions.ExternalAssemblies 컬렉션에 해당 어셈블리를 추가해야 합니다. FlexChart를 사용하려면 FlexChart와 해당 종속 항목에 참조를 추가해야 합니다.


카테고리 별 매출 코드

// adding assemblies referenced in script
​
_printDocument.ScriptingOptions.ExternalAssemblies.Add(typeof(Form).Assembly.ManifestModule.Name);
_printDocument.ScriptingOptions.ExternalAssemblies.Add(typeof(C1.Win.Chart.FlexChart).Assembly.ManifestModule.Name);
_printDocument.ScriptingOptions.ExternalAssemblies.Add("System.Data.dll");
_printDocument.ScriptingOptions.ExternalAssemblies.Add("System.Xml.dll");
​
// create DataTable and assign it to document tag as data source for charts
​
// the "dataTable" tag will be used to pass the DataTable value to the RenderImage object
​
Tag newTag = new Tag("dataTable", GetDataSource(), typeof(System.Data.DataTable));
_printDocument.Tags.Add(newTag);
​
// define data schema
var dataSource = CreateDemoDataSource();
​
var dsCategories = new DataSet(dataSource,
    "SELECT c.CategoryName, p.ProductName, p.UnitPrice, p.UnitsInStock " +
    "FROM Products p, Categories c " +
    "WHERE p.CategoryID = c.CategoryID " +
    "ORDER BY c.CategoryName, p.ProductName");
​
// add data source and data set to the document: this will preserve the data binding if the document is saved as c1d/c1dx
_printDocument.DataSchema.DataSources.Add(dataSource);
_printDocument.DataSchema.DataSets.Add(dsCategories);
​
var rt = new RenderTable();
​
// set header 1
rt.Cells[1, 0].Text = "[Fields!CategoryName.Value]";
​
// set header 2
rt.Cells[2, 0].Text = "Product:";
rt.Cells[2, 1].Text = "Sales:";
​
// set data row
rt.Cells[3, 0].Text = "[Fields!ProductName.Value]";
rt.Cells[3, 1].Text = "[string.Format(\"{0:C}\",Fields!UnitPrice.Value * Fields!UnitsInStock.Value)]";
​
// create group by category name
TableVectorGroup tvg = rt.RowGroups[0, 4];
tvg.DataBinding.DataSource = dsCategories;
tvg.DataBinding.Grouping.Expressions.Add("Fields!CategoryName.Value");
​
// add data rows
tvg = rt.RowGroups[3, 1];
tvg.DataBinding.DataSource = dsCategories;
​
var raLeft = new RenderArea();
raLeft.Children.Add(rt);
​
RenderArea raContainer = new RenderArea();
​
// arrange areas side-by-side
raContainer.Stacking = StackingRulesEnum.InlineLeftToRight;
​
// try to keep single category on a single page
raContainer.SplitVertBehavior = SplitBehaviorEnum.SplitIfLarge;
​
// set data bindings
raContainer.DataBinding.DataSource = dsCategories;
raContainer.DataBinding.Grouping.Expressions.Add("Fields!CategoryName.Value");
​
RenderImage ri = new RenderImage(_printDocument);
​
​
// show all exceptions and warnings for script debug
_printDocument.ThrowExceptionOnError = true;
_printDocument.AddWarningsWhenErrorInScript = true;
​
ri.FormatDataBindingInstanceScript = @"! see VB script below !";
​
RenderArea raRight = new RenderArea();
raRight.Children.Add(ri);
raContainer.Children.Add(raLeft);
raContainer.Children.Add(raRight);
_printDocument.Body.Children.Add(raContainer);
​
// generate document
_printDocument.Generate();
 

Visual Basic 스크립트(위 코드에서 FormatDataBindingInstanceScript에 사용됨)는 차트 객체를 생성하여 해당 객체에 데이터 소스를 할당한 다음 차트를 이미지로 변환합니다.


차트를 만드는 Visual Basic 스크립트

' create chart
Dim chart as C1.Win.Chart.FlexChart = New C1.Win.Chart.FlexChart()
​
' set chart parameters
chart.BindingX = ""ProductName"" ' assign the name of the property that contains X values for the series
chart.Binding = ""UnitPrice""           ' assign the name of the property for the DataSource property to use in axis labels
chart.BindingContext = New System.Windows.Forms.BindingContext()
chart.AxisX.Style.Font = new System.Drawing.Font(""Tahoma"", 7, System.Drawing.FontStyle.Regular)
chart.ChartType = C1.Chart.ChartType.Column ' show vertical bars
chart.BackColor = Color.White
chart.AxisX.OverlappingLabels = C1.Chart.OverlappingLabels.Auto ' hide overlapping labels
chart.AxisX.LabelAngle = 90 ' the rotation angle of the axis labels
​
Dim series = new C1.Win.Chart.Series()
chart.Series.Add(series)
​
' set chart size
Dim size as Size = New Size(340, 270)
​
' assign data source
Dim documentTag = DirectCast(Document.Tags, TagCollection)
Dim dt = DirectCast(documentTag!dataTable.Value, System.Data.DataTable) ' get the "dataTable" tag
Dim dv as System.Data.DataView = New System.Data.DataView(dt)
Dim filter as String = ""CategoryName = "" & RenderObject.Original.Parent.DataBinding.Parent.Fields!CategoryName.Value & ""'""
dv.RowFilter = filter
chart.DataSource = dv
​
' create the chart image
Dim ms as IO.MemoryStream = New IO.MemoryStream()
chart.SaveImage(ms, C1.Win.Chart.ImageFormat.Png, size.Width, size.Height)
​
' assign the image to RenderImage object
Dim ri as RenderImage = DirectCast(RenderObject, RenderImage)
​
ri.Image = Image.FromStream(ms)


마찬가지로, 참조에 사용자 정의 어셈블리를 추가하고 문서 스크립트에서 클래스를 사용할 수 있습니다.

C1PrintDocument 구조는 계층적이고 비교적 간단합니다. 데이터는 바인딩을 사용하여 설정되고 데이터의 모양을 정의하는 그룹으로 표시됩니다.

RenderObject에서 상속되는 각 요소는 기능을 확장하는 스크립트 실행을 지원합니다. C1PrintDocument는 여러 레이아웃 옵션을 사용하여 간단한 보고서를 만들거나 바인딩되지 않은 텍스트를 인쇄하기 위한 뛰어난 도구입니다.

.NET 4.5.2 버전과 .NET 5, .NET 6 등의 버전에서 C1PrintDocument를 계속 지원할 것입니다.


전체 샘플 코드 받기


C1PrintDocument 사용 문서에서 자세한 내용을 찾아볼 수 있습니다.





지금 바로 ComponentOne을 다운로드하여 직접 테스트해보세요!

c1.png

 
  • 페이스북으로 공유
  • 트위터로  공유
  • 구글플러스로 공유
  • 카카오톡으로 보내기

댓글목록

등록된 댓글이 없습니다.

그레이프시티 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기

태그

인기글

더보기
  • 인기 게시물이 없습니다.
그레이프시티 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기
이메일 : sales-kor@grapecity.com | 전화 : 1670-0583 | 경기도 안양시 동안구 시민대로 230, B-703(관양동, 아크로타워) 그레이프시티(주) 대표자 : 허경명 | 사업자등록번호 : 123-84-00981 | 통신판매업신고번호 : 2013-경기안양-00331 Copyright ⓒ 2022 GrapeCity inc.