Reports for WinForms | ComponentOne
Reports for WinForms Top Tips / C1PrintDocument Tips
In This Topic
    C1PrintDocument Tips
    In This Topic

    The following tips relate to the C1PrintDocument component.

    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()
    
    ' Create a RenderTable object:    
    Dim rt As New RenderTable()
    
    ' Adjust table's properties so that columns are auto-sized:    
    ' 1) By default, table width is set to parent (page) width,    
    ' for auto-sizing we must change it to auto (so based on content):    
    rt.Width = Unit.Auto    
    ' 2) Set ColumnSizingMode to Auto (default means Fixed for columns):    
    rt.ColumnSizingMode = TableSizingModeEnum.Auto    
    ' That's it, now the table's columns will be auto-sized.
    
    ' Turn table grid lines on to better see auto-sizing, add some padding:    
    rt.Style.GridLines.All = LineDef.[Default]    
    rt.CellStyle.Padding.All = "2mm"
    
    ' Add the table to the document    
    doc.Body.Children.Add(rt)
    
    ' Add some data    
    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"
    

    To write code in C#

    C#
    Copy Code
    C1PrintDocument doc = new C1PrintDocument();    
     
    // Create a RenderTable object:    
     RenderTable rt = new RenderTable();
    
    // adjust table's properties so that columns are auto-sized:    
    // 1) By default, table width is set to parent (page) width,    
    // for auto-sizing you must change it to auto (so based on content):    
    rt.Width = Unit.Auto;    
    // 2) Set ColumnSizingMode to Auto (default means Fixed for columns):    
    rt.ColumnSizingMode = TableSizingModeEnum.Auto;    
    // That's it, now the table's columns will be auto-sized.
    
    // Turn table grid lines on to better see auto-sizing, add some padding:    
    rt.Style.GridLines.All = LineDef.Default;    
    rt.CellStyle.Padding.All = "2mm";
    
    // Add the table to the document    
    doc.Body.Children.Add(rt);
    
    // Add some data    
    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 Reports 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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim RenderText rt As New RenderText("testing...")    
    rt.Style.TextColor = Color.Red
    

    To write code in C#

    C#
    Copy Code
    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:

      To write code in Visual Basic

      Visual Basic
      Copy Code
      Dim doc As New C1PrintDocument()    
      ' Add and set up default code style:    
      Dim sDefault As Style = doc.Style.Children.Add()    
      sDefault.FontName = "Courier New"    
      sDefault.FontSize = 10    
      ' Add and set up keyword style:    
      Dim sKeyword As Style = doc.Style.Children.Add()    
      sKeyword.FontName = "Courier New"    
      sKeyword.FontSize = 10    
      sKeyword.TextColor = Color.Blue    
      ' Add and set up comments style:    
      Dim sComment As Style = doc.Style.Children.Add()    
      sComment.FontName = "Courier New"    
      sComment.FontSize = 10    
      sComment.FontItalic = True    
      sComment.TextColor = Color.Green
      

      To write code in C#

      C#
      Copy Code
      C1PrintDocument doc = new C1PrintDocument();    
      // Add and set up default code style:    
      Style sDefault = doc.Style.Children.Add();    
      sDefault.FontName = "Courier New";    
      sDefault.FontSize = 10;    
      // Add and set up keyword style:    
      Style sKeyword = doc.Style.Children.Add();    
      sKeyword.FontName = "Courier New";    
      sKeyword.FontSize = 10;    
      sKeyword.TextColor = Color.Blue;    
      // Add and set up comments style:    
      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:

      To write code in Visual Basic

      Visual Basic
      Copy Code
      Dim codeLine As New RenderParagraph()    
      MessageBox.Show("Hello World!")    
      ' say hi to the world    
      Dim p1 As New ParagraphText("MessageBox")    
      p1.Style.AmbientParent = sKeyword    
      codeLine.Content.Add(p1)    
      Dim p2 As New ParagraphText(".Show(""Hello World!""); ")    
      p2.Style.AmbientParent = sDefault    
      codeLine.Content.Add(p2)    
      Dim p3 As New ParagraphText("// say hi to the world")    
      p3.Style.AmbientParent = sComment    
      codeLine.Content.Add(p3)    
      doc.Body.Children.Add(codeLine)
      

      To write code in C#

      C#
      Copy Code
      RenderParagraph codeLine = new RenderParagraph();    
      MessageBox.Show("Hello World!"); 
      // say hi to the 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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()    
    doc.PageLayout.PageHeader = New RenderText("[iif(PageNo=1, ""First page"", ""Page "" & PageNo & "" of "" & PageCount)]")
    

    To write code in C#

    C#
    Copy Code
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()    
    doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp    
    doc.PageLayout.PageHeader = New RenderText("[PageNo==1 ? ""First page"" : ""Page "" + PageNo + "" of "" + PageCount]")
    

    To write code in C#

    C#
    Copy Code
    C1PrintDocument doc = new C1PrintDocument();    
    doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp;    
    doc.PageLayout.PageHeader = new RenderText("[PageNo==1 ? \"First page\" : \"Page \" + PageNo + \" of \" + 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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    doc.PageLayout.PageHeader = New RenderText("[iif(PageNo=1, ""First page"", " & "string.Format(""Page {0} of {1}"", PageNo, PageCount))]")
    

    To write code in C#

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

    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Public Class Customer    
        Private _name As String    
        Private _balance As Integer    
        Public Sub New(ByVal name As String, ByVal balance As Integer)    
            _name = name    
            _balance = balance    
        End Sub    
        Public ReadOnly Property Name() As String    
            Get    
                Return _name    
            End Get    
        End Property    
        Public ReadOnly Property Balance() As Integer    
            Get    
                Return _balance    
            End Get    
        End Property    
    End Class
    

    To write code in C#

    C#
    Copy Code
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    ' build sample list of customers    
    Dim customers As New List(Of Customer)()   
    Dim rnd As New Random(DateTime.Now.Second)    
    For i As Integer = 0 To 599    
        customers.Add(New Customer("Customer_" & (i + 1).ToString(), rnd.[Next](-1000, 1000)))    
    Next
    

    To write code in C#

    C#
    Copy Code
    // build sample list of customers    
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()    
    Dim rt As New RenderTable()    
    ' Define data binding on table rows:     
    rt.RowGroups(0, 1).DataBinding.DataSource = customers    
    ' Bind column 0 to Name:    
    rt.Cells(0, 0).Text = "[Fields!Name.Value]"    
    ' Bind column 1 to Balance:    
    rt.Cells(0, 1).Text = "[Fields(""Balance"").Value]"    
    ' Add the table to the document:    
    doc.Body.Children.Add(rt)
    

    To write code in C#

    C#
    Copy Code
    C1PrintDocument doc = new C1PrintDocument();    
    RenderTable rt = new RenderTable();    
    // Define data binding on table rows:     
    rt.RowGroups[0, 1].DataBinding.DataSource = customers;    
    // Bind column 0 to Name:    
    rt.Cells[0, 0].Text = "[Fields!Name.Value]";    
    // Bind column 1 to Balance:    
    rt.Cells[0, 1].Text = "[Fields(\"Balance\").Value]";    
    // Add the table to the document:    
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    ' Define data binding on table rows:     
    rt.RowGroups(0, 1).DataBinding.DataSource = customers)
    

    To write code in C#

    C#
    Copy Code
    // Define data binding on table rows:     
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    ' Bind column 0 to Name:    
    rt.Cells(0, 0).Text = "[Fields!Name.Value]"    
    ' Bind column 1 to Balance:    
    rt.Cells(0, 1).Text = "[Fields(""Balance"").Value]"
    

    To write code in C#

    C#
    Copy Code
    // Bind column 0 to Name:    
    rt.Cells[0, 0].Text = "[Fields!Name.Value]";    
    // Bind column 1 to 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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    rt.Cells(0, 1).Style.TextColorExpr = "iif(Fields!Balance.Value < 0, Color.Red, Color.Blue)"
    

    To write code in C#

    C#
    Copy Code
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    ro.Style.BackColorExpr = "[iif(PageNo > 10, Color.Red, Color.Green)]"
    

    To write code in C#

    C#
    Copy Code
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp
    

    To write code in C#

    C#
    Copy Code
    doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp;
    

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

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()    
    doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp    
    Dim rt As New RenderTable()    
    ' Define data binding on table rows:     
    rt.RowGroups(0, 1).DataBinding.DataSource = customers    
    ' Bind column 0 to Name:    
    rt.Cells(0, 0).Text = " [Fields[""Name""].Value]"    
    ' Bind column 1 to Balance:    
    rt.Cells(0, 1).Text = " [Fields[""Balance""].Value]"    
    rt.Cells(0, 1).Style.TextColorExpr = "(int)(Fields[""Balance""].Value) < 0 ? Color.Red : Color.Blue"    
    ' Add the table to the document:    
    doc.Body.Children.Add(rt)
    

    To write code in C#

    C#
    Copy Code
    C1PrintDocument doc = new C1PrintDocument();    
    doc.ScriptingOptions.Language = C1.C1Preview.Scripting.ScriptLanguageEnum.CSharp;    
    RenderTable rt = new RenderTable();    
    // Define data binding on table rows:     
    rt.RowGroups[0, 1].DataBinding.DataSource = customers;    
    // Bind column 0 to Name:    
    rt.Cells[0, 0].Text = " [Fields[\"Name\"].Value]";    
    // Bind column 1 to Balance:    
    rt.Cells[0, 1].Text = " [Fields[\"Balance\"].Value]";    
    rt.Cells[0, 1].Style.TextColorExpr = "(int)(Fields[\"Balance\"].Value) < 0 ? Color.Red : Color.Blue";    
    // Add the table to the document:    
    doc.Body.Children.Add(rt);
    

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

    Tip 5: Customizing Databound 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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()    
    Dim rt As New RenderTable()    
    ' Next 3 lines set table up for horizontal expansion:    
    rt.Width = Unit.Auto    
    rt.ColumnSizingMode = TableSizingModeEnum.Auto    
    rt.SplitHorzBehavior = SplitBehaviorEnum.SplitIfNeeded    
    ' Define data binding on table columns:    
    rt.ColGroups(0, 1).DataBinding.DataSource = customers    
    ' Bind column 0 to Name:    
    rt.Cells(0, 0).Text = "[Fields!Name.Value]"    
    ' Bind column 1 to 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)"    
    ' Add the table to the document:    
    doc.Body.Children.Add(rt)
    

    To write code in C#

    C#
    Copy Code
    C1PrintDocument doc = new C1PrintDocument();    
    RenderTable rt = new RenderTable();    
    // Next 3 lines set table up for horizontal expansion:    
    rt.Width = Unit.Auto;    
    rt.ColumnSizingMode = TableSizingModeEnum.Auto;    
    rt.SplitHorzBehavior = SplitBehaviorEnum.SplitIfNeeded;    
    // Define data binding on table columns:     
    rt.ColGroups[0, 1].DataBinding.DataSource = customers;    
    // Bind column 0 to Name:    
    rt.Cells[0, 0].Text = "[Fields!Name.Value]";    
    // Bind column 1 to 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)";    
    // Add the table to the document:    
    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:

    To write code in Visual Basic

    Visual Basic
    Copy Code
    Dim doc As New C1PrintDocument()    
    Dim ri As New RenderImage()    
    ri.Control = Me.button1    
    doc.Body.Children.Add(ri)
    

    To write code in C#

    C#
    Copy Code
    C1PrintDocument doc = new C1PrintDocument();    
    RenderImage ri = new RenderImage();    
    ri.Control = this.button1;    
    doc.Body.Children.Add(ri);