PrintDocument for WinForms
PrintDocument
ベストプラクティス > PrintDocument

Some of the best practices to follow while working with the PrintDocument component are listed below:

Tip 1: Setting RenderTable Width and Auto-sizing

When setting up RenderTable objects it is important to take into account that by default, the width of a RenderTable is set to the width of its containing object which usually defaults to the width of the page. This sometimes leads to unexpected results, in particular if you want the table's columns to auto-size; to do so the table's Width must be explicitly set to Auto (which for a table means the sum of the columns widths).

For example, the following code builds a document with an auto-sized table:

C#
コードのコピー
C1PrintDocument doc = new C1PrintDocument();   
// RenderTableオブジェクトを作成します。   
 RenderTable rt = new RenderTable();
// 列が自動サイズになるようにテーブルのプロパティを調整します。
// 1) デフォルトでは、テーブルの幅は親(ページ)の幅に設定されています。
// 自動サイズ設定の場合、コンテンツに基づいて自動サイズに変更する必要があります。
rt.Width = Unit.Auto;   
// 2) ColumnSizingModeをAutoに設定します(デフォルトは列に対して固定を意味します):
rt.ColumnSizingMode = TableSizingModeEnum.Auto;   
// これで、テーブルの列のサイズが自動化されます。
// テーブルのグリッド線を表示にして、自動サイズ設定を見やすくし、パディングを追加します。
rt.Style.GridLines.All = LineDef.Default;   
rt.CellStyle.Padding.All = "2mm";
// ドキュメントにテーブルを追加します。
doc.Body.Children.Add(rt);
// いくつかのデータを追加します。
rt.Cells[0, 0].Text = "aaa";   
rt.Cells[0, 1].Text = "bbbbbbbbbb";   
rt.Cells[0, 2].Text = "cccccc";   
rt.Cells[1, 0].Text = "aaa aaa aaa";   
rt.Cells[1, 1].Text = "bbbbb";   
rt.Cells[1, 2].Text = "cccccc cccccc";   
rt.Cells[2, 2].Text = "zzzzzzzzzzzzzzz zz z";

For a complete example see the AutoSizeTable sample installed with PrintDocument for WinForms.

Tip 2: Using Parent/Ambient Parent Styles to Optimize Memory Usage

When rendered, paragraphs and other C1PrintDocument objects have styles that can be modified "inline". For example, like this:

C#
コードのコピー
RenderText rt = new RenderText("testing...");   
rt.Style.TextColor = Color.Red;

This is fine for small documents or styles used just once in the whole document. For large documents and styles used throughout the document, it is much better to use parent styles using the following pattern:

  1. Identify the styles you will need. For instance, if you are building a code pretty printing application, you may need the following styles:
    • Default code style
    • Language keyword style
    • Comments style
  2. Add a child style to the document's root Style for each of the styles identified in step 1, like this for example:
    C#
    コードのコピー
    C1PrintDocument doc = new C1PrintDocument();   
    // デフォルトのコードスタイルを追加して設定します。   
    Style sDefault = doc.Style.Children.Add();   
    sDefault.FontName = "Courier New";   
    sDefault.FontSize = 10;   
    // キーワードのスタイルを追加して設定します。   
    Style sKeyword = doc.Style.Children.Add();   
    sKeyword.FontName = "Courier New";   
    sKeyword.FontSize = 10;   
    sKeyword.TextColor = Color.Blue;   
    // コメントのスタイルを追加して設定します。   
    Style sComment = doc.Style.Children.Add();   
    sComment.FontName = "Courier New";   
    sComment.FontSize = 10;   
    sComment.FontItalic = true;   
    sComment.TextColor = Color.Green;
    
  3. In your code, whenever you create a C1PrintDocument element representing a part of source code you're pretty printing, assign the corresponding style to the element style's Parent, for example:
    C#
    コードのコピー
    RenderParagraph codeLine = new RenderParagraph();   
    MessageBox.Show("Hello World!");  
    ParagraphText p1 = new ParagraphText("MessageBox");   
    p1.Style.AmbientParent = sKeyword;   
    codeLine.Content.Add(p1);   
    ParagraphText p2 = new ParagraphText(".Show(\"Hello World!\"); ");   
    p2.Style.AmbientParent = sDefault;   
    codeLine.Content.Add(p2);   
    ParagraphText p3 = new ParagraphText("// say hi to the world");   
    p3.Style.AmbientParent = sComment;   
    codeLine.Content.Add(p3);   
    doc.Body.Children.Add(codeLine);
    

That's it, you're done. If you consistently assign your predefined styles to AmbientParent (or Parent, see below) properties of various document elements, your code will be more memory efficient (and more easily manageable).

You may have noted that you assigned your predefined styles to the AmbientParent property of the elements' styles. Remember, in C1PrintDocument styles, ambient properties affect content of elements, and by default propagate via elements hierarchies so nested objects inherit ambient style properties from their parents (unless a style's AmbientParent property is explicitly set). In contrast to that, non-ambient properties affect elements' "decorations" and propagate via styles own hierarchy determined by styles parents so for a non-ambient style property to affect a child object, its style's Parent property must be set.

The usefulness of this distinction is best demonstrated by an example: suppose you have a RenderArea containing a number of RenderText objects. To draw a border around the whole render area you would set the area's Style.Borders. Because Borders is a non-ambient property, it will draw the border around the area but will not propagate to the nested text objects and will not draw borders around each text which is normally what you'd want. On the other hand, to set the font used to draw all texts within the area, you again would set the area's Style.Font. Because Font, unlike Borders, is an ambient property it will propagate to all nested text objects and affect them again usually achieving the desired result. So when you are not setting styles parent/ambient parent properties things normally "just work". But when you do use styles' parents you must take the distinction between ambient and non-ambient style properties into consideration.

Note that for cases when you want to affect both ambient and non-ambient properties of an object, you may use the Style.Parents (note the plural) property it sets both Parent and AmbientParent properties on a style to the specified value.

Tip 3: Using Expressions to Customize Page Headers

Sometimes it is necessary to use a different page header for the first or last page of a document. While C1PrintDocument provides a special feature for that (see the PageLayouts note the plural property), for cases when the difference between the header on the first and subsequent pages is only in the header text, using an expression may be the best approach. For instance if you want to print "First page" as the first page's header and "Page x of y" for other pages, the following code may be used:

C#
コードのコピー
C1PrintDocument doc = new C1PrintDocument();   
doc.PageLayout.PageHeader = new RenderText( "[iif(PageNo=1, \"First page\", \"Page \" & PageNo & \" of \" & PageCount)]");

In the string representing the expression in the code above, the whole expression is enclosed in square brackets - they indicate to the document rendering engine that what is inside should be treated as an expression (those are adjustable via TagOpenParen and TagCloseParen properties on the document).

Whatever is inside those brackets should represent a valid expression in the current C1PrintDocument's script/expression language. By default it is VB.NET (but can be changed to C# see below), hence you use a VB.NET iif function to adjust your page header text depending on the page number. Here's the expression that is actually seen/executed by the document engine:

iif(PageNo=1, "First page", "Page " & PageNo & " of " & PageCount)

Because you must specify this expression as a C# or VB.NET string when assigning it to the page header text, you have to escape double quotes. Variables PageNo and PageCount are provided by the document engine (for a complete list of special variables accessible in different contexts in expressions, see the Expressions, Scripts, Tags topic).

As was mentioned, the default expression/script language used by C1PrintDocument is VB.NET. But C# can also be used as the expression language. For that, the C1PrintDocument Language property must be set to C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp. Using C# as the expression language, our example would look like this:

C#
コードのコピー
doc.PageLayout.PageHeader = new RenderText("[iif(PageNo=1, \"First page\", " + "string.Format(\"Page {0} of {1}\", PageNo, PageCount))]");

There were two changes:

Note that expressions are real .NET language expressions, and all normally accessible features of the corresponding language may be used in expressions. For instance instead of string concatenation you could have used the string.Format method as follows:

C#
コードのコピー
Type your example code here. It will be automatically colorized when you switch to Preview or build the help system.

Tip 4: Data Binding and Expressions

Databound render objects together with expressions are among the less known but extremely powerful C1PrintDocument features. In this example, you'll build a document with a render table data bound to a list of objects in memory. The list elements will represent Customer records with just two (for brevity) fields Name and Balance:

C#
コードのコピー
public class Customer   
{   
  private string _name;   
  private int _balance;   
  public Customer(string name, int balance)   
  {   
    _name = name;   
    _balance = balance;   
  }   
  public string Name { get { return _name; } }   
  public int Balance { get { return _balance; } }   
}

The following code can be used to create a list of customer records and fill it with some sample data:

C#
コードのコピー
// 顧客のサンプルリストを作成します。
List<Customer> customers = new List<Customer>();   
Random rnd = new Random(DateTime.Now.Second);   
  for (int i = 0; i < 600; i++)   
    customers.Add(new Customer("Customer_" + (i+1).ToString(), rnd.Next(-1000, 1000)));

Note that the Balance field's value ranges from -1000 to 1000 so the field allows negative values. This will be used to demonstrate a new C1PrintDocument feature, style expressions, below.

The following code prints the list created above as a RenderTable in a C1PrintDocument:

C#
コードのコピー
C1PrintDocument doc = new C1PrintDocument();   
RenderTable rt = new RenderTable();   
// テーブル行のデータ連結を定義します。    
rt.RowGroups[0, 1].DataBinding.DataSource = customers;   
// 列0をNameに連結します。
rt.Cells[0, 0].Text = "[Fields!Name.Value]";   
// 列1をBalanceに連結します。   
rt.Cells[0, 1].Text = "[Fields(\"Balance\").Value]";   
// ドキュメントにテーブルを追加します。
doc.Body.Children.Add(rt);                   

Databinding is achieved with just 3 lines of code. The first line defines a row group on the table, starting at row 0 and including just that one row:

C#
コードのコピー
// テーブル行のデータ連結を定義します。
rt.RowGroups[0, 1].DataBinding.DataSource = customers;

The other two lines show two syntactically different but equivalent ways of binding a table cell to a data field:

C#
コードのコピー
// 列0をNameに連結します。  
rt.Cells[0, 0].Text = "[Fields!Name.Value]";   
// 列1をBalanceに連結します。   
rt.Cells[0, 1].Text = "[Fields(\"Balance\").Value]";

As noted, the "Fields!Name" notation is just syntactic sugar for referencing the element called Name in the Fields array, and allows to avoid the need to use escaped double quotes.

Now, remember that the Balance field in the sample data set can be positive or negative. The following line will make all negative Balance values appear red colored in the document:

C#
コードのコピー
rt.Cells[0, 1].Style.TextColorExpr = "iif(Fields!Balance.Value < 0, Color.Red, Color.Blue)";

This demonstrates a new C1PrintDocument feature style expressions. Starting with 2009 v3 release, all style properties have matching expression properties (ending in "Expr"), which allow you to define an expression that would be used at run time to calculate the effective corresponding style property. While this feature is independent of data binding, it can be especially useful in data bound documents as shown here.

Style expressions allow the use of predefined C1PrintDocument tags related to pagination. For instance, the following code may be used to print a render object ro on red background if it appears on page with number greater than 10 and on green background otherwise:

C#
コードのコピー
ro.Style.BackColorExpr = "[iif(PageNo > 10, Color.Red, Color.Green)]";

Finally, it should be noted that while VB.NET is the default expression language in C1PrintDocument, C# can be used instead if the Language property is set on the document:

C#
コードのコピー
doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp;

With this in mind, our current sample may be rewritten as follows:

C#
コードのコピー
C1PrintDocument doc = new C1PrintDocument();   
doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp;   
RenderTable rt = new RenderTable();   
// テーブル行のデータ連結を定義します。    
rt.RowGroups[0, 1].DataBinding.DataSource = customers;   
// 列0をNameに連結します。  
rt.Cells[0, 0].Text = " [Fields[\"Name\"].Value]";   
// 列1をBalanceに連結します。   
rt.Cells[0, 1].Text = " [Fields[\"Balance\"].Value]";   
rt.Cells[0, 1].Style.TextColorExpr = "(int)(Fields[\"Balance\"].Value) < 0 ? Color.Red : Color.Blue";   
// ドキュメントにテーブルを追加します。  
doc.Body.Children.Add(rt);                   

Note the following as compared to code that used VB.NET as expressions/scripting language:

Tip 5: Customizing Data-bound Table Columns

In C1PrintDocument's tables, you can have data bound columns rather than rows. Consider our example from previous section it only takes a few changes to make the data bound table expand horizontally rather than vertically. Here's the code rewritten to show customer's name in the first row of the table, customer's balance in the second row, with each column corresponding to a customer entry:

C#
コードのコピー
C1PrintDocument doc = new C1PrintDocument();   
RenderTable rt = new RenderTable();   
// 次の3行は、テーブルを水平方向に展開します。
rt.Width = Unit.Auto;   
rt.ColumnSizingMode = TableSizingModeEnum.Auto;   
rt.SplitHorzBehavior = SplitBehaviorEnum.SplitIfNeeded;   
// テーブル列のデータ連結を定義します。    
rt.ColGroups[0, 1].DataBinding.DataSource = customers;   
// 列0をNameに連結します。 
rt.Cells[0, 0].Text = "[Fields!Name.Value]";   
// 列1をBalanceに連結します。
rt.Cells[1, 1].Text = "[Fields(\"Balance\").Value]";   
// Print negative values in red, positive in blue:   
rt.Cells[0, 1].Style.TextColorExpr = "iif(Fields!Balance.Value < 0, Color.Red, Color.Blue)";   
// ドキュメントにテーブルを追加します。
doc.Body.Children.Add(rt);

Note the following changes:

Tip 6: Including WinForms controls in a document

It is easy to include a snapshot of a WinForms control from your application in a document that is generated. To do that, use a RenderImage object and its Control property. For instance if your form contains a button button1, this code will include a snapshot of that button within a document:

C#
コードのコピー
C1PrintDocument doc = new C1PrintDocument();   
RenderImage ri = new RenderImage();   
ri.Control = this.button1;   
doc.Body.Children.Add(ri);