Difference between revisions of "Synchronize tables' size"

From Catglobe Wiki
Jump to: navigation, search
m (Design detail)
(First version)
Line 35: Line 35:
  
 
[[Image:BigTable-Split.png]]
 
[[Image:BigTable-Split.png]]
 +
 +
== Design decision ==
 +
* Using [[XmlVisitor]] to build report from xml
 +
* Accept the current Xml format but MUST be easy to extend the code to support for new format in case we change the format of xml later
 +
  
 
== Design detail ==
 
== Design detail ==
Line 43: Line 48:
 
* There are 2 kinds of report builder: ''DocumentBuilder'' and ''PresentationBuilder'' => for now only ''DocumentBuilder'' is implemented
 
* There are 2 kinds of report builder: ''DocumentBuilder'' and ''PresentationBuilder'' => for now only ''DocumentBuilder'' is implemented
 
* The ''DocumentBuilder'' use [[XmlVisitor]] to build report from and xml declaration, thus it need to provide a class that implement ''XmlVisitMethodImpl'' which is ''DocumentBuildImpl''
 
* The ''DocumentBuilder'' use [[XmlVisitor]] to build report from and xml declaration, thus it need to provide a class that implement ''XmlVisitMethodImpl'' which is ''DocumentBuildImpl''
 +
 +
=== Apply 2 phases process ===
 +
* For each table chart, after generating, the generated table and its source information will be stored, see the code below:
 +
 +
<source lang="csharp">
 +
      protected virtual void BuildTableDiagram(XElement element, T context)
 +
      {
 +
        // TODO: how should we treat error, let it crash now
 +
        int diagramId = Convert.ToInt32(element.GetRequireAttribute("diagramid").Value);
 +
 +
        // Fetch diagram
 +
        Domain.Reports.Diagram diagram = Domain.Reports.Diagram.GetByResourceId(diagramId);
 +
 +
        // Create diagram info => suppose that diagram must exists
 +
        DiagramInfo diagramInfo = diagram.CreateInfo();
 +
 +
        // Build all tables
 +
        var tables = ChartBuilder.BuildChart<ChartTable>(
 +
            new ChartBuildInfo
 +
            {
 +
              Chart = Diagram.BuildChartObject(diagramInfo),
 +
              Style = diagram.Style ?? StyleSheet.GetDefault(), // TODO: Merge with report style
 +
              InlineStyles = diagramInfo.ChartLayoutInfo.TableLayoutInfo.InlineStyles,
 +
              Type = ReportType.Pdf,
 +
              CreateInnerTable = CreateInnerTable,
 +
            });
 +
 +
        // Add result to current session
 +
        foreach (var table in tables)
 +
        {
 +
            AddTable(table, context);
 +
            _allChartTables.Add(new GeneratedTableInfo
 +
                                  {
 +
                                      Table = table,
 +
                                      BuildInfo = diagramInfo,
 +
                                  });
 +
        }
 +
      }
 +
</source>
 +
 +
* After report built, inspect the list of generated diagram and apply the size synchronization:
 +
 +
<source lang="csharp">
 +
      public virtual void FinalizeBuild()
 +
      {
 +
        // Available print area
 +
        float contentWidth = ContentWidth;
 +
 +
        // List of tables that need to be split
 +
        List<GeneratedTableInfo> toBeSplitTables = new List<GeneratedTableInfo>();
 +
        List<GeneratedTableInfo> nonSplitTables = new List<GeneratedTableInfo>();
 +
 +
        // Find the max content width first
 +
        float dataContentWidth = 0;
 +
        foreach (GeneratedTableInfo tableInfo in _allChartTables)
 +
        {
 +
            ChartTable table = tableInfo.Table;
 +
            // Make sure nothing wrong
 +
            Debug.Assert(table.ColumnWidthInfo != null &&
 +
              table.ColumnWidthInfo.DataColumnWidths != null &&
 +
              table.ColumnWidthInfo.DataColumnWidths.Length > 0);
 +
            // Find the max content width
 +
            if (table.ColumnWidthInfo.DataColumnWidths[0] > dataContentWidth)
 +
              dataContentWidth = table.ColumnWidthInfo.DataColumnWidths[0];
 +
            // Add to split tables if the width exceed content width
 +
            if (table.ColumnWidthInfo.TotalWidth > contentWidth)
 +
              toBeSplitTables.Add(tableInfo);
 +
            else
 +
              nonSplitTables.Add(tableInfo);
 +
        }
 +
 +
        // Apply to all non split table first
 +
        foreach (GeneratedTableInfo tableInfo in nonSplitTables)
 +
            tableInfo.Table.ColumnWidthInfo = TableColumnWidthInfo.Create(
 +
              tableInfo.Table.ColumnWidthInfo, dataContentWidth);
 +
 +
        // Now split the big guys into smaller ones
 +
        foreach (GeneratedTableInfo tableInfo in toBeSplitTables)
 +
            SplitTable(tableInfo, dataContentWidth);
 +
      }
 +
 +
</source>
 +
 +
=== Sample results ===
 +
[[Image: Mutitable-SameFormat.png]]
 +
  
 
== Document revisions ==
 
== Document revisions ==

Revision as of 09:33, 11 February 2010

Introduction

The new report engine is designed in a loose-coupling way that Report and Diagram are 2 separate matter. It means that Report know just enough about the Diagram to get it built while the Diagram has no idea about the Report that is using it.

But, when there are more than one table diagram in a Report, the customer doesn't want these tables having different format just like the below example. Multitable-DifferentFormat.png

The customer doesn't want to see a big table whose content is cropped due to not enough width as below image either BigTableNotBreak.png

So we need to define a way to synchronize the content of tables in same document and also make sure a big table can be split into multiple sub-table with consistent content.

2 phases layout

The method is being used in WPF and Silverlight for layout objects

  • First phase: ask all child objects about its expected layout
  • Second phase: base all these layout information => proceed with customization

First phase

  • Report builder let all table diagram built without any constraint
  • Table diagram is built with its own simple constraint:
    • All data cells MUST not be wrapped and have the same width. This means that all data cells (or data columns) would have the same maximum width that is required to display the longest data value.
    • The highest level row header is 3 time the width of data cell
    • 1 level higher, down to haft size

Second phase

Base on the width of all table and the available page width, we can determine the optimum data cell's width and also the tables that need to be split.

  • Find the maximum width of all data cells among all tables
  • Rebuild the column widths and set to all tables that do not need to be split
  • Split tables that bigger than page size base on some rules
    • abs/pct MUST be group together
    • second level option texts SHOULD be group together

Example:

BigTable-Split.png

Design decision

  • Using XmlVisitor to build report from xml
  • Accept the current Xml format but MUST be easy to extend the code to support for new format in case we change the format of xml later


Design detail

ReportBuilder.png

  • As Diagram builder, Report build takes input as an ReportBuildInfo
  • The main entry point is the Build method of class ReportBuilder
  • There are 2 kinds of report builder: DocumentBuilder and PresentationBuilder => for now only DocumentBuilder is implemented
  • The DocumentBuilder use XmlVisitor to build report from and xml declaration, thus it need to provide a class that implement XmlVisitMethodImpl which is DocumentBuildImpl

Apply 2 phases process

  • For each table chart, after generating, the generated table and its source information will be stored, see the code below:
      protected virtual void BuildTableDiagram(XElement element, T context)
      {
         // TODO: how should we treat error, let it crash now
         int diagramId = Convert.ToInt32(element.GetRequireAttribute("diagramid").Value);

         // Fetch diagram
         Domain.Reports.Diagram diagram = Domain.Reports.Diagram.GetByResourceId(diagramId);

         // Create diagram info => suppose that diagram must exists
         DiagramInfo diagramInfo = diagram.CreateInfo();

         // Build all tables
         var tables = ChartBuilder.BuildChart<ChartTable>(
            new ChartBuildInfo
            {
               Chart = Diagram.BuildChartObject(diagramInfo),
               Style = diagram.Style ?? StyleSheet.GetDefault(), // TODO: Merge with report style
               InlineStyles = diagramInfo.ChartLayoutInfo.TableLayoutInfo.InlineStyles,
               Type = ReportType.Pdf,
               CreateInnerTable = CreateInnerTable,
            });

         // Add result to current session
         foreach (var table in tables)
         {
            AddTable(table, context);
            _allChartTables.Add(new GeneratedTableInfo
                                   {
                                      Table = table,
                                      BuildInfo = diagramInfo,
                                   });
         }
      }
  • After report built, inspect the list of generated diagram and apply the size synchronization:
      public virtual void FinalizeBuild()
      {
         // Available print area
         float contentWidth = ContentWidth;

         // List of tables that need to be split
         List<GeneratedTableInfo> toBeSplitTables = new List<GeneratedTableInfo>();
         List<GeneratedTableInfo> nonSplitTables = new List<GeneratedTableInfo>();

         // Find the max content width first
         float dataContentWidth = 0;
         foreach (GeneratedTableInfo tableInfo in _allChartTables)
         {
            ChartTable table = tableInfo.Table;
            // Make sure nothing wrong
            Debug.Assert(table.ColumnWidthInfo != null &&
               table.ColumnWidthInfo.DataColumnWidths != null &&
               table.ColumnWidthInfo.DataColumnWidths.Length > 0);
            // Find the max content width
            if (table.ColumnWidthInfo.DataColumnWidths[0] > dataContentWidth)
               dataContentWidth = table.ColumnWidthInfo.DataColumnWidths[0];
            // Add to split tables if the width exceed content width
            if (table.ColumnWidthInfo.TotalWidth > contentWidth)
               toBeSplitTables.Add(tableInfo);
            else
               nonSplitTables.Add(tableInfo);
         }

         // Apply to all non split table first
         foreach (GeneratedTableInfo tableInfo in nonSplitTables)
            tableInfo.Table.ColumnWidthInfo = TableColumnWidthInfo.Create(
               tableInfo.Table.ColumnWidthInfo, dataContentWidth);

         // Now split the big guys into smaller ones
         foreach (GeneratedTableInfo tableInfo in toBeSplitTables)
            SplitTable(tableInfo, dataContentWidth);
      }

Sample results

Mutitable-SameFormat.png


Document revisions

Version No. Date Changed By Description Svn revision
0.1 11.02.2010 Nguyễn Trung Chính First version 60300