Source code for a simple .NET report application

This simplified .NET report program shows you the basic C# code you would need to create a .NET report application.

This C# program completes the basic needs for any Genero Report Writer program:

  • Creates the data model.
  • Loads the data into memory.
  • Configures the output of the report.
  • Streams the data to the report engine.

The report is a simple example of a C# report program. In a real world situation, the data would be read from another source, and an iterator would read the data into memory and into the data stream one record at a time.

using FourJs.Report.Runtime;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Sales
{

    //The XmlRoot annotation causes an global element declaration to be produced in the schema.
    [Serializable]
    [XmlRoot]
    public class Sales
    {
        //The XmlElement annotation maps a property to an XML element.

        //The Genero Engine does not support optional variables so setting "IsNullable = true" is mandatory.
        //If a variable needs to be optional then set the variable to null whenthere is no value.
        //If a primitive type needs to be optional then use the c# nullable type 
        //instead (e.g "int?" for "int").

        [XmlElementAttribute(IsNullable = true)]
        public String shopName;
        public int zipCode;
        public DateTime day;


        //This and any other public collection will be serialized in the order they are declared descending //recursively into the classes contained in the collections.
        [XmlElementAttribute(IsNullable = true)]
        public List<SalesItem> items;

        public Sales(String shopName, int zipCode, DateTime day)
        {
            this.items = new List<SalesItem>();
            this.shopName = shopName;
            this.zipCode = zipCode;
            this.day = day;
            int i = 0;
            items.Add(new SalesItem("Tablelamp", SalesItem.Category.Furniture, 23.00, null));
            items.Add(new SalesItem("Tablelamp", SalesItem.Category.Furniture, 267.00, items[i++]));
            items.Add(new SalesItem("Officechair", SalesItem.Category.Furniture, 155.00, items[i++]));
            items.Add(new SalesItem("Grandfather clock", SalesItem.Category.Furniture, 329.00, items[i++]));
            items.Add(new SalesItem("Scissors", SalesItem.Category.Supplies, 19.00, items[i++]));
            items.Add(new SalesItem("Measuring tape", SalesItem.Category.Supplies, 23.00, items[i++]));
            items.Add(new SalesItem("Sunglasses", SalesItem.Category.Travelling, 15.95, items[i++]));
            items.Add(new SalesItem("Penknife", SalesItem.Category.Travelling, 6.25, items[i++]));
            items.Add(new SalesItem("Ornateangel", SalesItem.Category.Art, 1.95, items[i++]));
        }

        /** Default Constructor that is required for deserialization.
            In this program this is never used.
        
         */
        public Sales()
        {

        }

        /**
            Runs the report using the design file specified in args[0] or "SalesList.4rp" otherwise.
            The program creates the file "SalesList.pdf" and opens it using
            System.Diagnostics.Process.Start which will typically
            invoke the Acrobat Reader.
        */
        static void Main(string[] args)
        {

            String designFile;
            String outputFilename = "SalesList.pdf";

            if (args.Length == 0)
            {
                designFile = "..\\SalesList.4rp";
            }
            else
            {
                designFile = args[0];
            }

            FormatHandler handler = new FormatWriter(outputFilename);
            PDFRenderer renderer = new PDFRenderer(handler);
            FourRpLayouter report = new FourRpLayouter(designFile, renderer);

            report.debugLevel = 9;
            Sales data = new Sales("Columbus Arts", 75038, new DateTime());

            report.runFromSerializable(data);

            // open the file
            System.Diagnostics.Process.Start(outputFilename);

        }

    }

    public class SalesItem
    {
        public enum Category { Furniture, Art, Supplies, Travelling };

        [XmlElementAttribute(IsNullable = true)]
        public String articleName;
        public Category category;
        public double price;
        public double runningTotal;

        //The previous item is passed to allow computing the running total.
        public SalesItem(String articleName, Category category, double price, SalesItem previousItem)
        {
            this.articleName = articleName;
            this.category = category;
            this.price = price;

            this.runningTotal = previousItem == null ? price : previousItem.runningTotal + price;
        }

        /** Default Constructor that is required for deserialization.
            In this program this is never used.
        */
        public SalesItem() { }

    }
}

An overview of the simple report

Two classes act as models to the list report. The classes are:
  • public class Sales - where Sales represents the sales of a particular shop on a particular day.
  • Public class SalesItem - where SalesItem represents the individual items sold in the shop on that day.

The classes are not required to implement any specific interfaces or to be extensions of a specific class. These classes are annotated to hint to the serializer how these objects are to be serialized to XML.

The C# program contains:
  • The Report Model Objects, which specify the data the report will include.
  • A main method to run the report using a simple list design and to view the result in PDF format.

Using this API is the method proposed in this example.

Mandatory C# classes to be imported

using FourJs.Report.Runtime;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;

XML annotations

XML annotations provide information in the XSD schema.
Serializable
The Serializable annotation marks classes that should be serialized.
[Serializable]
XmlRoot
The XmlRoot annotation causes a global element declaration to be produced in the schema.
...
[XmlRoot]
public class Sales {
...
XmlElementAttribute
The XmlElementAttribute annotation maps a property to an XML element.
   [XmlElementAttribute(IsNullable = true)]
   public String shopName;
Note: The Genero Report Engine does not support optional values. If a variable needs to be optional, use "isNullable=true" and set the variable to null when there is no value.

Genero Report Writer APIs and C# code highlights

The following are excerpts of C# report code that feed the report data and control the output.
  • Use a handler object.
    FormatHandler handler = new FormatWriter(outputFilename);
  • Define a renderer according to the expected output format. In this example, the PDFRenderer is specified. See Change report output format (C#) for a summary of your choices.
    PDFRenderer  renderer = new PDFRenderer(handler);
  • Create the report object specifying the report design file and the renderer object.
    FourRpLayouter report = new FourRpLayouter(designFile, renderer);
  • Specify the data.
    Sales data = new Sales("Columbus Arts", 75038, new DateTime());
  • Run the report.
    report.runFromSerializable(data);

    The following is an alternative method for running a report, using a data file (.xml) instead of a database.

    In our simple program, no iterator is specified. All the data is in memory, and the standard iterator goes through that list in memory, done internally by runFromSerializable. To see code that uses an iterator, examine the code in the OrderReportCSharp demo.

    report.runFromXML("my_data_file.xml");
  • Generate the report file on disk.
    File result = new File(outputFilename);