ComponentOne RichTextBox for WPF
Working with the C1Document Object / Creating Documents and Reports
In This Topic
    Creating Documents and Reports
    In This Topic

    To illustrate the process of creating a C1Document, we will walk through the steps required to implement a simple assembly documentation utility. In the following section learn how to implement create documents and reports in .NET 4.5.2 and .NET 5 versions.

    To start, create a new project and add a reference to the C1.WPF and C1.WPF.RichTextBox assemblies. Then edit the page constructor as follows:

    Visual Basic
    Copy Code
    Imports C1.WPF
    Imports C1.WPF.RichTextBox
    Imports C1.WPF.RichTextBox.Documents
    Public Partial Class MainPage
        Inherits UserControl
        ' C1RichTextBox that will display the C1Document
        Private _rtb As C1RichTextBox
        Public Sub New()
            ' Default initialization
            InitializeComponent()
            ' Create the C1RichTextBox and add it to the page
            _rtb = New C1RichTextBox()
            LayoutRoot.Children.Add(_rtb)
            ' Create document and show it in the C1RichTextBox
            _rtb.Document = DocumentAssembly(GetType(C1RichTextBox).Assembly)
            _rtb.IsReadOnly = True
        End Sub
    End Class
    
    C#
    Copy Code
    using C1.WPF;
    using C1.WPF.RichTextBox;
    using C1.WPF.RichTextBox.Documents;
    public partial class MainPage : UserControl
    {
      // C1RichTextBox that will display the C1Document
      C1RichTextBox _rtb;
      public MainPage()
      {
        // Default initialization
        InitializeComponent();
        // Create the C1RichTextBox and add it to the page
        _rtb = new C1RichTextBox();
        LayoutRoot.Children.Add(_rtb);
        // Create document and show it in the C1RichTextBox
        _rtb.Document = DocumentAssembly(typeof(C1RichTextBox).Assembly);
        _rtb.IsReadOnly = true;
      }
    }
    

    The code creates the C1RichTextBox and assigns its C1RichTextBox.Document property to the result of a call to the DocumentAssembly method. It then makes the control read-only so users can't change the report.

    The DocumentAssembly method takes an Assembly as argument and builds a C1Document containing the assembly documentation. Here is the implementation:

    Visual Basic
    Copy Code
    Private Function DocumentAssembly(asm As Assembly) As C1Document
        ' Create document
        Dim doc As New C1Document()
        doc.FontFamily = New FontFamily("Tahoma")
        ' Assembly
        doc.Blocks.Add(New Heading1("Assembly" & vbCr & vbLf + asm.FullName.Split(","C)(0)))
        ' Types
        For Each t As Type In asm.GetTypes()
            DocumentType(doc, t)
        Next
        ' Done
        Return doc
    End Function
    
    C#
    Copy Code
          
    C1Document DocumentAssembly(Assembly asm)
    {
      // Create document
      C1Document doc = new C1Document();
      doc.FontFamily = new FontFamily("Tahoma");
      // Assembly
      doc.Blocks.Add(new Heading1("Assembly\r\n" + asm.FullName.Split(',')[0]));
      // Types
      foreach (Type t in asm.GetTypes())
        DocumentType(doc, t);
      // Done
      return doc;
    }
    

    The method starts by creating a new C1Document object and setting its C1TextElement.FontFamily property. This will be the default value for all text elements added to the document.

    Next, the method adds a Heading1 paragraph containing the assembly name to the new document's Blocks collection. Blocks are elements such as paragraphs and list items that flow down the document. They are similar to "div" elements in HTML. Some document elements contain an Inlines collection instead. These collections contain elements that flow horizontally, similar to "span" elements in HTML.

    The Heading1 class inherits from C1Paragraph and adds some formatting. We will add several such classes to the project, for normal paragraphs and headings 1 through 4.

    The Normal paragraph is a C1Paragraph that takes a content string in its constructor:

    Visual Basic
    Copy Code
    Class Normal
        Inherits C1Paragraph
        Public Sub New(text As String)
            Me.Inlines.Add(New C1Run() With { _
                Key .Text = text _
            })
            Me.Padding = New Thickness(30, 0, 0, 0)
            Me.Margin = New Thickness(0)
        End Sub
    End Class
    
    C#
    Copy Code
    class Normal : C1Paragraph
    {
      public Normal(string text)
      {
        this.Inlines.Add(new C1Run() { Text = text });
        this.Padding = new Thickness(30, 0, 0, 0);
        this.Margin = new Thickness(0);
      }
    }
    

    The Heading paragraph extends Normal and makes the text bold:

    Visual Basic
    Copy Code
    Class Heading
        Inherits Normal
        Public Sub New(text As String)
            MyBase.New(text)
            Me.FontWeight = FontWeights.Bold
        End Sub
    End Class
    
    C#
    Copy Code
    class Heading : Normal
    {
      public Heading(string text) : base(text)
      {
        this.FontWeight = FontWeights.Bold;
      }
    }
    

    Heading1 through Heading4 extend Heading to specify font sizes, padding, borders, and colors:

    Visual Basic
    Copy Code
    Class Heading1
        Inherits Heading
        Public Sub New(text As String)
            MyBase.New(text)
            Me.Background = New SolidColorBrush(Colors.Yellow)
            Me.FontSize = 24
            Me.Padding = New Thickness(0, 10, 0, 10)
            Me.BorderBrush = New SolidColorBrush(Colors.Black)
            Me.BorderThickness = New Thickness(3, 1, 1, 0)
        End Sub
    End Class
    Class Heading2
        Inherits Heading
        Public Sub New(text As String)
            MyBase.New(text)
            Me.FontSize = 18
            Me.FontStyle = FontStyles.Italic
            Me.Background = New SolidColorBrush(Colors.Yellow)
            Me.Padding = New Thickness(10, 5, 0, 5)
            Me.BorderBrush = New SolidColorBrush(Colors.Black)
            Me.BorderThickness = New Thickness(3, 1, 1, 1)
        End Sub
    End Class
    Class Heading3
        Inherits Heading
        Public Sub New(text As String)
            MyBase.New(text)
            Me.FontSize = 14
            Me.Background = New SolidColorBrush(Colors.LightGray)
            Me.Padding = New Thickness(20, 3, 0, 0)
        End Sub
    End Class
    Class Heading4
        Inherits Heading
        Public Sub New(text As String)
            MyBase.New(text)
            Me.FontSize = 14
            Me.Padding = New Thickness(30, 0, 0, 0)
        End Sub
    End Class
    
    C#
    Copy Code
    class Heading1 : Heading
    {
      public Heading1(string text) : base(text)
      {
        this.Background = new SolidColorBrush(Colors.Yellow);
        this.FontSize = 24;
        this.Padding = new Thickness(0, 10, 0, 10);
        this.BorderBrush = new SolidColorBrush(Colors.Black);
        this.BorderThickness = new Thickness(3, 1, 1, 0);
      }
    }
    class Heading2 : Heading
    {
      public Heading2(string text): base(text)
      {
        this.FontSize = 18;
        this.FontStyle = FontStyles.Italic;
        this.Background = new SolidColorBrush(Colors.Yellow);
        this.Padding = new Thickness(10, 5, 0, 5);
        this.BorderBrush = new SolidColorBrush(Colors.Black);
        this.BorderThickness = new Thickness(3, 1, 1, 1);
      }
    }
    class Heading3 : Heading
    {
      public Heading3(string text) : base(text)
      {
        this.FontSize = 14;
        this.Background = new SolidColorBrush(Colors.LightGray);
        this.Padding = new Thickness(20, 3, 0, 0);
      }
    }
    class Heading4 : Heading
    {
      public Heading4(string text): base(text)
      {
        this.FontSize = 14;
        this.Padding = new Thickness(30, 0, 0, 0);
      }
    }
    

    Now that we have classes for all paragraph types in the document, it's time to add the content. Recall that we used a DocumentType method in the first code block. Here is the implementation for that method:

    Visual Basic
    Copy Code
    Private Sub DocumentType(doc As C1Document, t As Type)
        ' Skip non-public/generic
        If Not t.IsPublic OrElse t.ContainsGenericParameters Then
            Return
        End If
        ' Type
        doc.Blocks.Add(New Heading2("Class " & Convert.ToString(t.Name)))
        ' Properties
        doc.Blocks.Add(New Heading3("Properties"))
        For Each pi As PropertyInfo In t.GetProperties()
            If pi.DeclaringType = t Then
                DocumentProperty(doc, pi)
            End If
        Next
          ' Methods
            doc.Blocks.Add(New Heading3("Methods"))
        For Each mi As MethodInfo In t.GetMethods()
            If mi.DeclaringType = t Then
                DocumentMethod(doc, mi)
            End If
        Next
            ' Events
            doc.Blocks.Add(New Heading3("Events"))
            For Each ei As EventInfo In t.GetEvents()
            If ei.DeclaringType = t Then
                DocumentEvent(doc, ei)
            End If
        Next
    End Sub
    
    C#
    Copy Code
    void DocumentType(C1Document doc, Type t)
    {
      // Skip non-public/generic
      if (!t.IsPublic || t.ContainsGenericParameters)
        return;
      // Type
      doc.Blocks.Add(new Heading2("Class " + t.Name));
      // Properties
      doc.Blocks.Add(new Heading3("Properties"));
      foreach (PropertyInfo pi in t.GetProperties())
      {
        if (pi.DeclaringType == t)
          DocumentProperty(doc, pi);
      }
      // Methods
      doc.Blocks.Add(new Heading3("Methods"));
      foreach (MethodInfo mi in t.GetMethods())
      {
        if (mi.DeclaringType == t)
          DocumentMethod(doc, mi);
      }
      // Events
      doc.Blocks.Add(new Heading3("Events"));
      foreach (EventInfo ei in t.GetEvents())
      {
        if (ei.DeclaringType == t)
          DocumentEvent(doc, ei);
      }
    }
    

    The method adds a Heading2 paragraph with the class name and then uses reflection to enumerate all the public properties, events, and methods in the type. The code for these methods is simple:

    Visual Basic
    Copy Code
    Private Sub DocumentProperty(doc As C1Document, pi As PropertyInfo)
        If pi.PropertyType.ContainsGenericParameters Then
            Return
        End If
        doc.Blocks.Add(New Heading4(pi.Name))
        Dim text = String.Format("public {0} {1} {{ {2}{3} }}", pi.PropertyType.Name, pi.Name, If(pi.CanRead, "get; ", String.Empty), If(pi.CanWrite, "set; ", String.Empty))
        doc.Blocks.Add(New Normal(text))
    End Sub
    
    C#
    Copy Code
    void DocumentProperty(C1Document doc, PropertyInfo pi)
    {
      if (pi.PropertyType.ContainsGenericParameters)
        return;
      doc.Blocks.Add(new Heading4(pi.Name));
      var text = string.Format("public {0} {1} {{ {2}{3} }}",
        pi.PropertyType.Name,
        pi.Name,
        pi.CanRead ? "get; " : string.Empty,
        pi.CanWrite ? "set; " : string.Empty);
      doc.Blocks.Add(new Normal(text));
    }
    

    The method adds a Heading4 paragraph containing the property name, then some Normal text containing the property type, name, and accessors.

    The methods used for documenting events and properties are analogous:

    Visual Basic
    Copy Code
    Private Sub DocumentMethod(doc As C1Document, mi As MethodInfo)
        If mi.IsSpecialName Then
            Return
        End If
        doc.Blocks.Add(New Heading4(mi.Name))
        Dim parms = New StringBuilder()
        For Each parm As var In mi.GetParameters()
            If parms.Length > 0 Then
                parms.Append(", ")
            End If
            parms.AppendFormat("{0} {1}", parm.ParameterType.Name, parm.Name)
        Next
        Dim text = String.Format("public {0} {1}({2})", mi.ReturnType.Name, mi.Name, parms.ToString())
        doc.Blocks.Add(New Normal(text))
    End Sub
    Private Sub DocumentEvent(doc As C1Document, ei As EventInfo)
        doc.Blocks.Add(New Heading4(ei.Name))
        Dim text = String.Format("public {0} {1}", ei.EventHandlerType.Name, ei.Name)
        doc.Blocks.Add(New Normal(text))
    End Sub
    
    C#
    Copy Code
    void DocumentMethod(C1Document doc, MethodInfo mi)
    {
      if (mi.IsSpecialName)
        return;
      doc.Blocks.Add(new Heading4(mi.Name));
      var parms = new StringBuilder();
      foreach (var parm in mi.GetParameters())
      {
        if (parms.Length > 0)
          parms.Append(", ");
        parms.AppendFormat("{0} {1}", parm.ParameterType.Name, parm.Name);
      }
      var text = string.Format("public {0} {1}({2})",
        mi.ReturnType.Name,
        mi.Name,
        parms.ToString());
      doc.Blocks.Add(new Normal(text));
    }
    void DocumentEvent(C1Document doc, EventInfo ei)
    {
      doc.Blocks.Add(new Heading4(ei.Name));
      var text = string.Format("public {0} {1}",
        ei.EventHandlerType.Name,
        ei.Name);
      doc.Blocks.Add(new Normal(text));
    }
    

    If you run the project now, you will see a window like the one shown below:

    Appearance of the image after running the project

    The resulting document can be viewed and edited in the C1RichTextBox like any other. It can also be exported to HTML using the C1RichTextBox.Html property in the C1RichTextBox, or copied through the clipboard to applications such as Microsoft Word or Excel.

    You could use the same technique to create reports based on data from a database. In addition to formatted text, the C1Document object model supports the following features:        

    • Lists
      Lists are created by adding C1List objects to the document. The C1List object has a C1List.ListItems property that contains C1ListItem objects, which are also blocks.
    • Hyperlinks
      Hyperlinks are created by adding C1Hyperlink objects to the document. The C1Hyperlink object has an C1Span.Inlines property that contains a collection of runs (typically C1Run elements that contain text), and a NavigateUrl property that determines the action to be taken when the hyperlink is clicked.
    • Images
      Images and other FrameworkElement objects are created by adding C1BlockUIContainer objects to the document. The C1BlockUIContainer object has a C1BlockUIContainer.Child property that can be set to any FrameworkElement object.
      Note that not all objects can be exported to HTML. Images are a special case that the HTML filter knows how to handle.

    To start, create a new project and add a reference to the C1.WPF and C1.WPF.RichTextBox assemblies. Then edit the page constructor as follows:

    C#
    Copy Code
    using C1.WPF.RichTextBox;
    using C1.WPF.RichTextBox.Documents;
    
    
        public partial class DocumentAndReport : Window
        {
            public DocumentAndReport()
            {
                InitializeComponent();
    
                // Create document and show it in the C1RichTextBox
                richTextBox.Document = DocumentCreator.DocumentAssembly(typeof(C1RichTextBox).Assembly);
    
                richTextBox.IsReadOnly = true;
            }
        }
    

    The code creates the C1RichTextBox and assigns its C1RichTextBox.Document property to the result of a call to the DocumentAssembly method. It then makes the control read-only so users can't change the report.

    The DocumentAssembly method takes an Assembly as argument and builds a C1Document containing the assembly documentation. Here is the implementation:

    C#
    Copy Code
    public static C1Document DocumentAssembly(Assembly asm)
    {
        // Create document
        C1Document doc = new C1Document();
        doc.FontFamily = new FontFamily("Tahoma");
    
        // Assembly
        doc.Blocks.Add(new Heading1("Assembly\r\n" + asm.FullName.Split(',')[0]));
    
        // Types
        foreach (Type t in asm.GetTypes())
            DocumentType(doc, t);
    
        // Done
        return doc;
    

    The method starts by creating a new C1Document object and setting its C1TextElement.FontFamily property. This will be the default value for all text elements added to the document.

    Next, the method adds a Heading1 paragraph containing the assembly name to the new document's Blocks collection. Blocks are elements such as paragraphs and list items that flow down the document. They are similar to "div" elements in HTML. Some document elements contain an Inlines collection instead. These collections contain elements that flow horizontally, similar to "span" elements in HTML.

    The Heading1 class inherits from C1Paragraph and adds some formatting. We will add several such classes to the project, for normal paragraphs and headings 1 through 4.

    The Normal paragraph is a C1Paragraph that takes a content string in its constructor. The Heading paragraph extends Normal and makes the text bold:

    C#
    Copy Code
    class Normal : C1Paragraph
    {
        public Normal(string text)
        {
            this.Inlines.Add(new C1Run() { Text = text });
            this.Padding = new Thickness(30, 0, 0, 0);
            this.Margin = new Thickness(0);
        }
    }
    
    class Heading : Normal
    {
        public Heading(string text) : base(text)
        {
            this.FontWeight = FontWeights.Bold;
        }
    }
    

     

    Heading1 through Heading4 extend Heading to specify font sizes, padding, borders, and colors:

    C#
    Copy Code
    class Heading1 : Heading
    {
        public Heading1(string text) : base(text)
        {
            this.Background = new SolidColorBrush(Colors.Yellow);
            this.FontSize = 24;
            this.Padding = new Thickness(0, 10, 0, 10);
            this.BorderBrush = new SolidColorBrush(Colors.Black);
            this.BorderThickness = new Thickness(3, 1, 1, 0);
        }
    }
    class Heading2 : Heading
    {
        public Heading2(string text) : base(text)
        {
            this.FontSize = 18;
            this.FontStyle = FontStyles.Italic;
            this.Background = new SolidColorBrush(Colors.Yellow);
            this.Padding = new Thickness(10, 5, 0, 5);
            this.BorderBrush = new SolidColorBrush(Colors.Black);
            this.BorderThickness = new Thickness(3, 1, 1, 1);
        }
    }
    class Heading3 : Heading
    {
        public Heading3(string text) : base(text)
        {
            this.FontSize = 14;
            this.Background = new SolidColorBrush(Colors.LightGray);
            this.Padding = new Thickness(20, 3, 0, 0);
        }
    }
    class Heading4 : Heading
    {
        public Heading4(string text) : base(text)
        {
            this.FontSize = 14;
            this.Padding = new Thickness(30, 0, 0, 0);
        }
    

    Now that we have classes for all paragraph types in the document, it's time to add the content. Recall that we used a DocumentType method in the first code block. Here is the implementation for that method:

    C#
    Copy Code
    static void DocumentType(C1Document doc, Type t)
    {
        // Skip non-public/generic
        if (!t.IsPublic || t.ContainsGenericParameters)
            return;
    
        // Type
        doc.Blocks.Add(new Heading2("Class " + t.Name));
    
        // Properties
        doc.Blocks.Add(new Heading3("Properties"));
        foreach (PropertyInfo pi in t.GetProperties())
        {
            if (pi.DeclaringType == t)
                DocumentProperty(doc, pi);
        }
    
        // Methods
        doc.Blocks.Add(new Heading3("Methods"));
        foreach (MethodInfo mi in t.GetMethods())
        {
            if (mi.DeclaringType == t)
                DocumentMethod(doc, mi);
        }
    
        // Events
        doc.Blocks.Add(new Heading3("Events"));
        foreach (EventInfo ei in t.GetEvents())
        {
            if (ei.DeclaringType == t)
                DocumentEvent(doc, ei);
        }
    

    The method adds a Heading2 paragraph with the class name and then uses reflection to enumerate all the public properties, events, and methods in the type. The code for these methods is simple:

    C#
    Copy Code
    // Gets property info
    static void DocumentProperty(C1Document doc, PropertyInfo pi)
    {
        if (pi.PropertyType.ContainsGenericParameters)
            return;
    
        doc.Blocks.Add(new Heading4(pi.Name));
    
        var text = string.Format("public {0} {1} {{ {2}{3} }}",
          pi.PropertyType.Name,
          pi.Name,
          pi.CanRead ? "get; " : string.Empty,
          pi.CanWrite ? "set; " : string.Empty);
        doc.Blocks.Add(new Normal(text));
    }
    

    The method adds a Heading4 paragraph containing the property name, then some Normal text containing the property type, name, and accessors.

    C#
    Copy Code
        // Gets method info
        static void DocumentMethod(C1Document doc, MethodInfo mi)
        {
            if (mi.IsSpecialName)
                return;
    
            doc.Blocks.Add(new Heading4(mi.Name));
            var parms = new StringBuilder();
            foreach (var parm in mi.GetParameters())
            {
                if (parms.Length > 0)
                    parms.Append(", ");
                parms.AppendFormat("{0} {1}", parm.ParameterType.Name, parm.Name);
            }
            var text = string.Format("public {0} {1}({2})",
              mi.ReturnType.Name,
              mi.Name,
              parms.ToString());
    
            doc.Blocks.Add(new Normal(text));
        }
    
    
        // Gets event info
        static void DocumentEvent(C1Document doc, EventInfo ei)
        {
            doc.Blocks.Add(new Heading4(ei.Name));
    
            var text = string.Format("public {0} {1}",
              ei.EventHandlerType.Name,
              ei.Name);
    
            doc.Blocks.Add(new Normal(text));
        }
    }
    

    The methods used for documenting events and properties are analogous:

    If you run the project now, you will see a window like the one shown below:

    The resulting document can be viewed and edited in the C1RichTextBox like any other. It can also be exported to HTML using the C1RichTextBox.Html property in the C1RichTextBox, or copied through the clipboard to applications such as Microsoft Word or Excel.

    You could use the same technique to create reports based on data from a database. In addition to formatted text, the C1Document object model supports the following features:        

    • Lists
      Lists are created by adding C1List objects to the document. The C1List object has a C1List.ListItems property that contains C1ListItem objects, which are also blocks.
    • Hyperlinks
      Hyperlinks are created by adding C1Hyperlink objects to the document. The C1Hyperlink object has an C1Span.Inlines property that contains a collection of runs (typically C1Run elements that contain text), and a NavigateUrl property that determines the action to be taken when the hyperlink is clicked.
    • Images
      Images and other FrameworkElement objects are created by adding C1BlockUIContainer objects to the document. The C1BlockUIContainer object has a C1BlockUIContainer.Child property that can be set to any FrameworkElement object.
      Note that not all objects can be exported to HTML. Images are a special case that the HTML filter knows how to handle.