using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using FastReport.Data;
using FastReport.Table;
using FastReport.Utils;
namespace FastReport.AdvMatrix
{
///
/// Describes how the even style is applied to a matrix.
///
public enum HeaderPriority
{
///
/// The even style is applied to matrix rows.
///
Rows,
///
/// The even style is applied to matrix columns.
///
Columns
}
///
/// Represents the matrix object that is used to print cross-table.
///
///
/// The matrix consists of the following elements: columns, rows and data cells. Each element is
/// represented by the descriptor. The class represents
/// columns and rows; data cells use dynamically created descriptors.
/// The property holds two root descriptors - Columns.Descriptor and Rows.Descriptor.
/// To create the matrix in a code, you should perform the following actions:
///
/// -
/// create an instance of the AdvMatrixObject and add it to the report;
///
/// -
/// create descriptors for columns and rows and add it to the
/// root descriptor using the matrix.Data.Columns.Descriptor and matrix.Data.Rows.Descriptor respectively;
///
/// -
/// call the method to create the matrix template
/// that will be used to create a result;
///
/// -
/// set the data cells content;
///
/// -
/// modify the matrix template (change captions, set the visual appearance).
///
///
/// To connect the matrix to a datasource, use the property. If
/// this property is not set, the result matrix will be empty. In this case you may use
/// the event handler to fill the matrix.
///
/// This example demonstrates how to create a matrix in a code.
///
/// // create an instance of AdvMatrixObject
/// AdvMatrixObject matrix = new AdvMatrixObject();
/// matrix.Name = "Matrix1";
/// // add it to the report title band of the first report page
/// matrix.Parent = (Report.Pages[0] as ReportPage).ReportTitle;
///
/// // create two nested column descriptors and a total
/// HeaderDescriptor column = new HeaderDescriptor("[MatrixDemo.Year]");
/// matrix.Data.Columns.Descriptor.Add(column);
/// HeaderDescriptor nestedColumn = new HeaderDescriptor("[MatrixDemo.Month]");
/// column.Add(nestedColumn);
/// HeaderDescriptor columnTotal = new HeaderDescriptor();
/// columnTotal.DisplayText = "Total";
/// matrix.Data.Columns.Descriptor.Add(columnTotal);
///
/// // create one row descriptor and a total
/// HeaderDescriptor row = new HeaderDescriptor("[MatrixDemo.Name]");
/// matrix.Data.Rows.Descriptor.Add(row);
/// HeaderDescriptor rowTotal = new HeaderDescriptor();
/// rowTotal.DisplayText = "Total";
/// matrix.Data.Rows.Descriptor.Add(rowTotal);
///
/// // connect matrix to a datasource
/// matrix.DataSource = Report.GetDataSource("MatrixDemo");
///
/// // create the matrix template
/// matrix.BuildTemplate();
///
/// // change the style
/// matrix.Style = "Gray";
///
/// // create data cells
/// string cellText = "[Sum([MatrixDemo.Revenue])]";
/// for (int i = matrix.Data.Rows.Size; i < matrix.ColumnCount; i++)
/// for (int j = matrix.Data.Columns.Size; j < matrix.RowCount; j++)
/// matrix[i, j].Text = cellText;
///
///
public partial class AdvMatrixObject : TableBase
{
#region Fields
private DataSourceBase dataSource;
private string style;
private bool saveVisible;
#endregion
#region Properties
///
/// Gets or sets a data source.
///
///
/// When you create the matrix in the designer by drag-drop data columns into it,
/// this property will be set automatically. However you need to set it if you create
/// the matrix in code.
///
[Category("Data")]
public DataSourceBase DataSource
{
get { return dataSource; }
set
{
if (dataSource != value)
{
if (dataSource != null)
dataSource.Disposed -= DataSource_Disposed;
if (value != null)
value.Disposed += DataSource_Disposed;
}
dataSource = value;
}
}
///
/// Gets the row filter expression.
///
///
/// This property can contain any valid boolean expression. If the expression returns false,
/// the corresponding data row will be skipped.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string Filter { get; set; }
///
/// Gets or sets a matrix style.
///
[Category("Appearance")]
[Editor("FastReport.TypeEditors.AdvMatrixStyleEditor, FastReport", typeof(UITypeEditor))]
public new string Style
{
get { return style; }
set
{
style = value;
TemplateBuilder.UpdateStyle(this);
}
}
///
/// Gets or sets even style priority for matrix cells.
///
[Category("Behavior")]
[DefaultValue(HeaderPriority.Rows)]
public new HeaderPriority EvenStylePriority { get; set; }
///
/// Gets or sets data row priority for matrix cells.
///
[Category("Behavior")]
[DefaultValue(HeaderPriority.Rows)]
public HeaderPriority DataRowPriority { get; set; }
///
/// Gets or sets a value indicating that empty matrix should be printed.
///
[Category("Behavior")]
[DefaultValue(true)]
public bool PrintIfEmpty { get; set; }
///
/// Gets or sets a value indicating that the matrix should reset its data on each report run.
///
///
/// Default value is false. In this case the matrix will use already prepared data when you refresh a report.
/// If you set it to true the matrix data will be reset on each report refresh. This also resets all user interaction such as
/// interactive sort and collapse state.
///
[Category("Behavior")]
[DefaultValue(false)]
public bool ResetDataOnRun { get; set; }
///
/// Gets or sets a script method name that will be used to handle the
/// event.
///
///
/// See the event for more details.
///
[Category("Build")]
public string ManualBuildEvent { get; set; }
///
/// Gets or sets a script method name that will be used to handle the
/// event.
///
///
/// See the event for more details.
///
[Category("Build")]
public string ModifyResultEvent { get; set; }
///
/// Allows to fill the matrix in code.
///
public event EventHandler ManualBuild;
///
/// Allows to modify the prepared matrix elements such as cells, rows, columns.
///
public event EventHandler ModifyResult;
///
/// Gets the object that holds the collection of descriptors used to build a matrix.
///
///
/// See the class for more details.
///
[Browsable(false)]
public MatrixData Data { get; private set; }
///
/// Gets or sets array of values that describes the currently printing column.
///
///
/// Use this property when report is running. It can be used to highlight matrix elements
/// depending on values of the currently printing column. To do this:
///
/// -
/// select the cell that you need to highlight;
///
/// -
/// click the "Highlight" button on the "Text" toolbar;
///
/// -
/// add a new highlight condition. Use the Matrix.ColumnValues to
/// refer to the value you need to analyze. Note: these values are array of dynamic,
/// so you don't need to cast it to actual type before making any comparisons. Example of highlight
/// condition: Matrix1.ColumnValues[0] == 2000.
///
///
///
///
[Browsable(false)]
public dynamic[] ColumnValues { get; set; }
///
/// Gets or sets array of values that describes the currently printing row.
///
///
/// Use this property when report is running. It can be used to highlight matrix elements
/// depending on values of the currently printing row. To do this:
///
/// -
/// select the cell that you need to highlight;
///
/// -
/// click the "Highlight" button on the "Text" toolbar;
///
/// -
/// add a new highlight condition. Use the Matrix.RowValues to
/// refer to the value you need to analyze. Note: these values are arrays of dynamic,
/// so you don't need to cast it to actual type before making any comparisons. Example of highlight
/// condition: Matrix1.RowValues[0] == "Andrew Fuller".
///
///
///
///
[Browsable(false)]
public dynamic[] RowValues { get; set; }
///
/// Gets or sets the index of currently printing column.
///
///
/// This property may be used to print even columns with alternate color. To do this:
///
/// -
/// select the cell that you need to highlight;
///
/// -
/// click the "Highlight" button on the "Text" toolbar;
///
/// -
/// add a new highlight condition that uses the Matrix.ColumnIndex,
/// for example: Matrix1.ColumnIndex % 2 == 1.
///
///
///
///
[Browsable(false)]
public int ColumnIndex { get; set; }
///
/// Gets or sets the index of currently printing row.
///
///
/// This property may be used to print even rows with alternate color. To do this:
///
/// -
/// select the cell that you need to highlight;
///
/// -
/// click the "Highlight" button on the "Text" toolbar;
///
/// -
/// add a new highlight condition that uses the Matrix.RowIndex,
/// for example: Matrix1.RowIndex % 2 == 1.
///
///
///
///
[Browsable(false)]
public int RowIndex { get; set; }
///
/// Gets or sets the data row index of currently printing header.
///
///
/// Use this value if you want to display the header value with its data row number, e.g. "1. Andrew Fuller".
/// To do this, set the header's DisplayExpression to something like this: Matrix1.RowNo + ". " + Value
///
[Browsable(false)]
public int RowNo { get; set; }
///
/// Gets or sets count of items in the currently printing header.
///
[Browsable(false)]
public int ItemCount { get; set; }
protected internal override bool IsCompilationNeeded => true;
internal Matrix.MatrixStyleSheet StyleSheet { get; private set; }
private BandBase ParentBand
{
get
{
BandBase parentBand = this.Band;
if (parentBand is ChildBand)
parentBand = (parentBand as ChildBand).GetTopParentBand;
return parentBand;
}
}
private DataBand FootersDataBand
{
get
{
DataBand dataBand = null;
if (ParentBand is GroupFooterBand)
dataBand = ((ParentBand as GroupFooterBand).Parent as GroupHeaderBand).GroupDataBand;
else if (ParentBand is DataFooterBand)
dataBand = ParentBand.Parent as DataBand;
return dataBand;
}
}
private bool IsOnFooter
{
get
{
DataBand dataBand = FootersDataBand;
if (dataBand != null)
{
return DataSource == dataBand.DataSource;
}
return false;
}
}
#endregion
#region Private Methods
private void CreateResultTable()
{
SetResultTable(new TableResult());
// assign properties from this object. Do not use Assign method: TableResult is incompatible with AdvMatrixObject.
ResultTable.OriginalComponent = OriginalComponent;
ResultTable.Alias = Alias;
ResultTable.Border = Border.Clone();
ResultTable.Fill = Fill.Clone();
ResultTable.Bounds = Bounds;
ResultTable.PrintOnParent = PrintOnParent;
ResultTable.RepeatHeaders = RepeatHeaders;
ResultTable.RepeatRowHeaders = RepeatRowHeaders;
ResultTable.RepeatColumnHeaders = RepeatColumnHeaders;
ResultTable.Layout = Layout;
ResultTable.WrappedGap = WrappedGap;
ResultTable.AdjustSpannedCellsWidth = AdjustSpannedCellsWidth;
ResultTable.SetReport(Report);
ResultTable.AfterData += ResultTable_AfterData;
}
private void DisposeResultTable()
{
ResultTable.Dispose();
SetResultTable(null);
}
private void ResultTable_AfterData(object sender, EventArgs e)
{
OnModifyResult(e);
}
private void DataSource_Disposed(object sender, EventArgs e)
{
dataSource = null;
}
private void WireEvents(bool wire)
{
if (IsOnFooter)
{
DataBand dataBand = FootersDataBand;
if (wire)
dataBand.BeforePrint += dataBand_BeforePrint;
else
dataBand.BeforePrint -= dataBand_BeforePrint;
}
}
private void dataBand_BeforePrint(object sender, EventArgs e)
{
bool firstRow = (sender as DataBand).IsFirstRow;
if (firstRow)
Data.Init();
object match = true;
if (!String.IsNullOrEmpty(Filter))
match = Report.Calc(Filter);
if (match is bool && (bool)match == true)
Data.ProcessDataRow();
}
#endregion
#region Protected Methods
///
protected override void DeserializeSubItems(FRReader reader)
{
if (String.Compare(reader.ItemName, "Columns", true) == 0)
reader.Read(Data.Columns);
else if (String.Compare(reader.ItemName, "Rows", true) == 0)
reader.Read(Data.Rows);
else
base.DeserializeSubItems(reader);
}
#endregion
#region Public Methods
///
public override void Assign(Base source)
{
BaseAssign(source);
AdvMatrixObject src = source as AdvMatrixObject;
DataSource = src.DataSource;
}
///
public override void Serialize(FRWriter writer)
{
if (writer.SerializeTo != SerializeTo.SourcePages)
{
writer.Write(Data.Columns);
writer.Write(Data.Rows);
}
base.Serialize(writer);
AdvMatrixObject c = writer.DiffObject as AdvMatrixObject;
if (DataSource != c.DataSource)
writer.WriteRef("DataSource", DataSource);
if (Filter != c.Filter)
writer.WriteStr("Filter", Filter);
if (Style != c.Style)
writer.WriteStr("Style", Style);
if (EvenStylePriority != c.EvenStylePriority)
writer.WriteValue("EvenStylePriority", EvenStylePriority);
if (DataRowPriority != c.DataRowPriority)
writer.WriteValue("DataRowPriority", DataRowPriority);
if (PrintIfEmpty != c.PrintIfEmpty)
writer.WriteBool("PrintIfEmpty", PrintIfEmpty);
if (ResetDataOnRun != c.ResetDataOnRun)
writer.WriteBool("ResetDataOnRun", ResetDataOnRun);
if (ManualBuildEvent != c.ManualBuildEvent)
writer.WriteStr("ManualBuildEvent", ManualBuildEvent);
if (ModifyResultEvent != c.ModifyResultEvent)
writer.WriteStr("ModifyResultEvent", ModifyResultEvent);
}
///
/// Creates or updates the matrix template.
///
///
/// Call this method after you modify the matrix descriptors using the object's properties.
///
public void BuildTemplate()
{
if (Data.Columns.Descriptor.Items.Count == 0)
Data.Columns.Descriptor.Add();
if (Data.Rows.Descriptor.Items.Count == 0)
Data.Rows.Descriptor.Add();
TemplateBuilder.BuildTemplate(this);
}
///
/// This method fires the ManualBuild event and the script code connected to the ManualBuildEvent.
///
/// Event data.
public void OnManualBuild(EventArgs e)
{
if (ManualBuild != null)
ManualBuild(this, e);
InvokeEvent(ManualBuildEvent, e);
}
///
/// This method fires the ModifyResult event and the script code connected to the ModifyResultEvent.
///
/// Event data.
public void OnModifyResult(EventArgs e)
{
if (ModifyResult != null)
ModifyResult(this, e);
InvokeEvent(ModifyResultEvent, e);
}
///
/// Toggles visible state of the column with specified index. For internal use only.
///
/// Index of column.
/// If true collapse all items.
/// If true expand all items.
public void ToggleColumnVisible(int index, bool collapseAll = false, bool expandAll = false)
{
Data.Columns.Data.ToggleVisible(index, collapseAll, expandAll);
}
///
/// Toggles visible state of the row with specified index. For internal use only.
///
/// Index of row.
/// If true collapse all items.
/// If true expand all items.
public void ToggleRowVisible(int index, bool collapseAll = false, bool expandAll = false)
{
Data.Rows.Data.ToggleVisible(index, collapseAll, expandAll);
}
///
/// Sort columns based on values of row with specified index. For internal use only.
///
/// Index of row.
/// The sort order.
public void SortColumnsByRow(int rowIndex, SortOrder sort)
{
Data.InteractiveSort.Row.Index = rowIndex;
Data.InteractiveSort.Row.Sort = sort;
}
///
/// Sort rows based on values of column with specified index. For internal use only.
///
/// Index of column.
/// The sort order.
public void SortRowsByColumn(int columnIndex, SortOrder sort)
{
Data.InteractiveSort.Column.Index = columnIndex;
Data.InteractiveSort.Column.Sort = sort;
}
#endregion
#region Report Engine
///
public override void ResetData()
{
Data.Reset();
}
///
public override void InitializeComponent()
{
base.InitializeComponent();
WireEvents(true);
}
///
public override void FinalizeComponent()
{
base.FinalizeComponent();
WireEvents(false);
}
///
public override string[] GetExpressions()
{
List expressions = new List();
TemplateBuilder.UpdateDescriptors(this);
expressions.AddRange(base.GetExpressions());
expressions.AddRange(Data.GetExpressions());
if (!String.IsNullOrEmpty(Filter))
expressions.Add(Filter);
return expressions.ToArray();
}
///
public override void SaveState()
{
saveVisible = Visible;
BandBase parent = Parent as BandBase;
if (!Visible || (parent != null && !parent.Visible))
return;
// create the result table that will be rendered in the preview
CreateResultTable();
Visible = false;
if (parent != null && !PrintOnParent)
{
parent.Height = Top;
parent.CanGrow = false;
parent.CanShrink = false;
parent.AfterPrint += ResultTable.GeneratePages;
}
}
///
public override void GetData()
{
base.GetData();
if (ResetDataOnRun)
Data.Reset();
if (!IsOnFooter && !Data.Completed)
{
// skip data processing if it was done before. It may happen if:
// - second pass of two-pass report
// - preview window needs to refresh a report due to user interaction
Data.Init();
Data.ProcessDataRows();
}
}
///
public override void OnAfterData(EventArgs e)
{
base.OnAfterData(e);
Data.Done();
if (PrintOnParent)
ResultTable.AddToParent(Parent);
}
///
public override void RestoreState()
{
BandBase parent = Parent as BandBase;
if (!saveVisible || (parent != null && !parent.Visible))
return;
if (parent != null && !PrintOnParent)
parent.AfterPrint -= ResultTable.GeneratePages;
DisposeResultTable();
Visible = saveVisible;
}
#endregion
///
/// Initializes a new instance of the class.
///
public AdvMatrixObject()
{
BaseName = "Matrix";
Data = new MatrixData(this);
Filter = "";
PrintIfEmpty = true;
StyleSheet = new Matrix.MatrixStyleSheet();
StyleSheet.Load(ResourceLoader.GetStream("cross.frss"));
style = "";
ManualBuildEvent = "";
ModifyResultEvent = "";
}
}
}