Difference between revisions of "Synchronize tables' size"
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
Contents
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.
The customer doesn't want to see a big table whose content is cropped due to not enough width as below image either
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:
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
- 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
Document revisions
Version No. | Date | Changed By | Description | Svn revision |
0.1 | 11.02.2010 | Nguyễn Trung Chính | First version | 60300 |