Simplicity is the element of the extraordinary. The first time I saw Telerik Reporting in action I was fascinated on how powerful yet simple this tool API seemed. Graphs

This tool offered numerous advantages over other reporting tools in the industry. Yet, I was hesitant due to past experience with products that the end of the day overpromised but mostly under deliver.

Today, I cannot convey enough how wonderful my experience has been with Telerik Reporting. This acquisition has given me the power to create complex reporting software in no time at all. Neither I needed to concern in the development environment; Telerik Reporting plays absolutely well with WPF, Silverlight, ASP.NET MVC and Windows Forms; with every report viewer offering a professional look and amazing modern appeal.

Unlike other tools,the software development experience with Telerik Reporting has been joyful. Developers tend to focus their attention directly to the API when they seek after understanding a development technology. Even in Forums, It is very evident the code centered characteristic of developer that get their hands on this tool for the first time. Nonetheless, Telerik Reporting counts with a powerful design viewer, which provides a fascinating “What You See Is What You Get” (WYSISYG) report generation capabilities.

The benefit of the design viewer does not only translate into design time efforts. It is also a utility of great assistance to understand report objects, and even adapt them at run-time. It has been a common practice to use the design viewer code behind – designer.cs – to elaborate and structure complex report objects. It is possible to create report objects without the aid of the design viewer. However, the design code behind is a great way to start on the learning path to complex reporting

In the below section I Introduce an example to provide on first hand the simplicity of the API.

 

Generating a Telerik Report Table at Run-Time

In order to introduce an example, I was found in the need of creating a sample data first; thus I created the below data source, which emulates a list of products

   1: public static class samplesource
   2:   {  
   3:         public static List<Product> GenerateSampleList()
   4:          {
   5:              List<Product> value = new List<Product>(); 
   6:              for(int c = 1; c <= 3; c++)
   7:              for (int p = 1; p <= 12; p++)
   8:              {                 Product prod = new Product { ProductName = "Product "+c , 
   9:                      Date = new DateTime(DateTime.Now.AddMonths(-p).Year, 
  10:                                     DateTime.Now.AddMonths(-p).Month, 1), 
  11:                      sale = anyRandomNumber  };
  12:                 
  13:                  value.Add(prod);
  14:              }
  15:              return value;
  16:          }
  17:          private static Random rand = new Random();
  18:          
  19:          public static double  anyRandomNumber
  20:          {
  21:              set {}
  22:              get
  23:              {
  24:                  return Convert.ToDouble(rand.Next(1000)); 
  25:              }        
  26:      
  27:          }
  28:       }

The first action item in creating a table object is to add a collection of row groups or column groups based on your requirement. In my example, I would used the number of periods / dates in my collection in the order to define the number of columns to display;

   1: Telerik.Reporting.Table ProductTable = new Telerik.Reporting.Table();
   2:             for(int col = 0; col < _source.GroupBy(d => d.Date).Select(l => l.Key)
   3:                     .Count(); col++) // add columns based on number of periods
   4:             {
   5:                 Telerik.Reporting.TableGroup tg  = new Telerik.Reporting.TableGroup();
   6:                 tg.Groupings.AddRange(new Telerik.Reporting.Data.Grouping[] 
   7:                                                     { new Telerik.Reporting.Data.Grouping() });
   8:                 ProductTable.ColumnGroups.Add(tg);
   9:                 ProductTable.Body.Columns.Add(new Telerik.Reporting
  10:                 .TableBodyColumn(Telerik.Reporting.Drawing.Unit.Inch(columnWidth)));
  11:             }

The TableGroup Object above instantiates a new group in the table object. Table groups are added to the table by the way of Table.Columns.Add or Table.Rows.Add

Simply enough, we could look at this table structure as a two dimensional array, thus we create two variables that will embody the row position and the column position in the table. Subsequently, We would create row groups based on the size of the collection, or number of products.

   1: int rowCounter = 0; 
   2: int columnCounter = 0;
   3: foreach (string prod in _source.GroupBy(pr => pr.ProductName.ToString()).Select(h => h.Key))
   4:    {
   5:        ProductTable.Body.Rows.Add(new TableBodyRow(Telerik.Reporting.Drawing.Unit.Inch(rowHeight)));
   6:         Telerik.Reporting.TextBox HeadRowName = new Telerik.Reporting.TextBox { Value = prod.ToString(), 
   7:             Size = new Telerik.Reporting.Drawing.SizeU(Telerik.Reporting.Drawing.Unit.Inch(HeaderWidth), 
   8:                   Telerik.Reporting.Drawing.Unit.Inch(rowHeight)) };
   9:         TableGroup tgItem = new TableGroup { ReportItem = HeadRowName };
  10:         ProductTable.RowGroups.Add(tgItem);
  11:         columnCounter = 0;
  12:         foreach (DateTime day in _source.GroupBy(d => d.Date).Select(l => l.Key))
  13:         {
  14:            
  15:             Telerik.Reporting.TextBox textbox = new Telerik.Reporting.TextBox 
  16:                 { Value = _source.Where(w => w.Date == day && w.ProductName == prod).Select(v => v.sale)
  17:               .FirstOrDefault().ToString(), Size = new SizeU(Unit.Inch(columnWidth), Unit.Inch(rowHeight)) };
  18:             ProductTable.Body.SetCellContent(rowCounter, columnCounter, textbox);
  19:             columnCounter++;
  20:         }
  21:   
  22:           rowCounter++;
  23:     }

No more complexity for us further. To display this object, we must add the Table into the Report Object. We could do this by simply adding a new report group, which would contain the report item. In Report.Cs this looks the following way:

   1: public void GenerateReportItems()
   2:         {
   3:              var Group1 = new Group(true);
   4:            
   5:             Group1.GroupFooter.Visible = false;
   6:             Group1.GroupHeader.Visible = true;
   7:             Group1.GroupHeader.PageBreak = PageBreak.After;
   8:             Group1.GroupHeader.Style.VerticalAlign = VerticalAlign.Middle;
   9:  
  10:            Group1.GroupHeader.KeepTogether = true;
  11:            System.Guid GroupGuid = System.Guid.NewGuid();
  12:            Group1.Name = GroupGuid.ToString();
  13:            Group1.GroupHeader.Items.Add((ReportItemBase)
  14:             (TableCreator.GetTableObject(samplesource.GenerateSampleList())));
  15:            this.Groups.Add(Group1);
  16:            Group1.Dispose();
  17:        }

Call this method in the constructor in order to display the report in the design view.

TableIncomplete

 

After effectively accomplish the task of creating a table object through the Telerik Reporting API, our table seems to lack a proper heading row. Here is how we can briefly introduce an extra row for a heading

   1: int rowCounter = 1; // Modified line
   2: int columnCounter = 0;
   3:  #region CustomHeader
   4:  int headerColumnCounter = 0;
   5:  ProductTable.Body.Rows.Add(new TableBodyRow(Telerik.Reporting.Drawing.Unit.Inch(rowHeight)));
   6:  Telerik.Reporting.TextBox HeadRowNameC = new Telerik.Reporting.TextBox { Value = "Date", 
   7:        Size = new Telerik.Reporting.Drawing.SizeU(Telerik.Reporting.Drawing.Unit.Inch(HeaderWidth),
   8:         Telerik.Reporting.Drawing.Unit.Inch(rowHeight)) };
   9:  TableGroup tgItemH = new TableGroup { ReportItem = HeadRowNameC };
  10:  ProductTable.RowGroups.Add(tgItemH);
  11:  foreach (DateTime day in _source.GroupBy(d => d.Date).Select(l => l.Key))
  12:  {
  13:      Telerik.Reporting.TextBox textboxh = new Telerik.Reporting.TextBox 
  14:              { Value = day.ToString("MMM, yyyy"), 
  15:              Size = new SizeU(Unit.Inch(columnWidth), Unit.Inch(rowHeight)) };
  16:      textboxh.Style.Font.Style = System.Drawing.FontStyle.Bold;
  17:      textboxh.Style.Font.Size = new Unit(8, UnitType.Point);
  18:      ProductTable.Body.SetCellContent(0, headerColumnCounter, textboxh);
  19:      headerColumnCounter++;
  20:  }
  21:  #endregion

TableComplete

   1: public static class TableCreator
   2:     {
   3:         private static double columnWidth = 0.64;
   4:         private static double rowHeight = 0.14;
   5:          private static double HeaderWidth = 1.00;
   6:  
   7:  
   8:         public static Telerik.Reporting.Table GetTableObject(List<Product> _source)
   9:         {
  10:            Telerik.Reporting.Table ProductTable = new Telerik.Reporting.Table();
  11:             for(int col = 0; col < _source.GroupBy(d => d.Date)
  12:             .Select(l => l.Key).Count(); col++) // add columns based on number of periods
  13:             {
  14:                 Telerik.Reporting.TableGroup tg  = new Telerik.Reporting.TableGroup();
  15:                 tg.Groupings.AddRange(new Telerik.Reporting.Data.Grouping[] 
  16:                                     { new Telerik.Reporting.Data.Grouping() });
  17:                   ProductTable.ColumnGroups.Add(tg);
  18:                 ProductTable.Body.Columns.Add( new Telerik.Reporting.TableBodyColumn
  19:                 (Telerik.Reporting.Drawing.Unit.Inch(columnWidth)));
  20:             }
  21:  
  22:              int rowCounter = 1; // Modified
  23:             int columnCounter = 0;
  24:  
  25:             #region CustomHeader
  26:             int headerColumnCounter = 0;
  27:             ProductTable.Body.Rows.Add(new 
  28:                     TableBodyRow(Telerik.Reporting.Drawing.Unit.Inch(rowHeight)));
  29:             Telerik.Reporting.TextBox HeadRowNameC = new Telerik.Reporting.TextBox 
  30:                 { Value = "Date", 
  31:                 Size = new Telerik.Reporting.Drawing.SizeU(
  32:                         Telerik.Reporting.Drawing.Unit.Inch(HeaderWidth), 
  33:                     Telerik.Reporting.Drawing.Unit.Inch(rowHeight)) };
  34:             TableGroup tgItemH = new TableGroup { ReportItem = HeadRowNameC };
  35:             ProductTable.RowGroups.Add(tgItemH);
  36:             foreach (DateTime day in _source.GroupBy(d => d.Date).Select(l => l.Key))
  37:             {
  38:                 Telerik.Reporting.TextBox textboxh = new Telerik.Reporting.TextBox 
  39:                 { Value = day.ToString("MMM, yyyy"), 
  40:                 Size = new SizeU(Unit.Inch(columnWidth), Unit.Inch(rowHeight)) };
  41:                 textboxh.Style.Font.Style = System.Drawing.FontStyle.Bold;
  42:                 textboxh.Style.Font.Size = new Unit(8, UnitType.Point);
  43:                 ProductTable.Body.SetCellContent(0, headerColumnCounter, textboxh);
  44:                 headerColumnCounter++;
  45:             }
  46:             #endregion
  47:  
  48:             foreach (string prod in _source.GroupBy(pr => pr.ProductName.ToString())
  49:                     .Select(h => h.Key))
  50:                 {
  51:                     ProductTable.Body.Rows.Add(new TableBodyRow
  52:                                 (Telerik.Reporting.Drawing.Unit.Inch(rowHeight)));
  53:                     Telerik.Reporting.TextBox HeadRowName = new Telerik.Reporting.TextBox 
  54:                         { Value = prod.ToString(), 
  55:                             Size = new Telerik.Reporting.Drawing.SizeU(
  56:                             Telerik.Reporting.Drawing.Unit
  57:                                                     .Inch(HeaderWidth), 
  58:                                 Telerik.Reporting.Drawing.Unit.Inch(rowHeight)) };
  59:                     TableGroup tgItem = new TableGroup { ReportItem = HeadRowName };
  60:                     ProductTable.RowGroups.Add(tgItem);
  61:                    columnCounter = 0;
  62:                    foreach (DateTime day in _source.GroupBy(d => d.Date).Select(l => l.Key))
  63:                     {
  64:                       
  65:                         Telerik.Reporting.TextBox textbox = new Telerik.Reporting.TextBox 
  66:                             { Value = _source.Where(w => w.Date == day && w.ProductName == prod)
  67:                                     .Select(v => v.sale).FirstOrDefault().ToString(), 
  68:                                 Size = new SizeU(Unit.Inch(columnWidth), Unit.Inch(rowHeight)) };
  69:                         ProductTable.Body.SetCellContent(rowCounter, columnCounter, textbox);
  70:                         columnCounter++;
  71:                     }
  72:                      rowCounter++;
  73:                 }
  74:             return ProductTable;  
  75:          }  
  76:     }