123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- 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
- {
- /// <summary>
- /// Describes how the even style is applied to a matrix.
- /// </summary>
- public enum HeaderPriority
- {
- /// <summary>
- /// The even style is applied to matrix rows.
- /// </summary>
- Rows,
- /// <summary>
- /// The even style is applied to matrix columns.
- /// </summary>
- Columns
- }
- /// <summary>
- /// Represents the matrix object that is used to print cross-table.
- /// </summary>
- /// <remarks>
- /// The matrix consists of the following elements: columns, rows and data cells. Each element is
- /// represented by the <b>descriptor</b>. The <see cref="HeaderDescriptor"/> class represents
- /// columns and rows; data cells use dynamically created descriptors.
- /// The <see cref="Data"/> property holds two root descriptors - <b>Columns.Descriptor</b> and <b>Rows.Descriptor</b>.
- /// <para/>To create the matrix in a code, you should perform the following actions:
- /// <list type="bullet">
- /// <item>
- /// <description>create an instance of the <b>AdvMatrixObject</b> and add it to the report;</description>
- /// </item>
- /// <item>
- /// <description>create descriptors for columns and rows and add it to the
- /// root descriptor using the <b>matrix.Data.Columns.Descriptor</b> and <b>matrix.Data.Rows.Descriptor</b> respectively;</description>
- /// </item>
- /// <item>
- /// <description>call the <see cref="BuildTemplate"/> method to create the matrix template
- /// that will be used to create a result;</description>
- /// </item>
- /// <item>
- /// <description>set the data cells content;</description>
- /// </item>
- /// <item>
- /// <description>modify the matrix template (change captions, set the visual appearance).</description>
- /// </item>
- /// </list>
- /// <para/>To connect the matrix to a datasource, use the <see cref="DataSource"/> property. If
- /// this property is not set, the result matrix will be empty. In this case you may use
- /// the <see cref="ManualBuild"/> event handler to fill the matrix.
- /// </remarks>
- /// <example>This example demonstrates how to create a matrix in a code.
- /// <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;
- /// </code>
- /// </example>
- public partial class AdvMatrixObject : TableBase
- {
- #region Fields
- private DataSourceBase dataSource;
- private string style;
- private bool saveVisible;
- #endregion
- #region Properties
- /// <summary>
- /// Gets or sets a data source.
- /// </summary>
- /// <remarks>
- /// 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.
- /// </remarks>
- [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;
- }
- }
- /// <summary>
- /// Gets the row filter expression.
- /// </summary>
- /// <remarks>
- /// This property can contain any valid boolean expression. If the expression returns <b>false</b>,
- /// the corresponding data row will be skipped.
- /// </remarks>
- [Category("Data")]
- [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
- public string Filter { get; set; }
- /// <summary>
- /// Gets or sets a matrix style.
- /// </summary>
- [Category("Appearance")]
- [Editor("FastReport.TypeEditors.AdvMatrixStyleEditor, FastReport", typeof(UITypeEditor))]
- public new string Style
- {
- get { return style; }
- set
- {
- style = value;
- TemplateBuilder.UpdateStyle(this);
- }
- }
- /// <summary>
- /// Gets or sets even style priority for matrix cells.
- /// </summary>
- [Category("Behavior")]
- [DefaultValue(HeaderPriority.Rows)]
- public new HeaderPriority EvenStylePriority { get; set; }
- /// <summary>
- /// Gets or sets data row priority for matrix cells.
- /// </summary>
- [Category("Behavior")]
- [DefaultValue(HeaderPriority.Rows)]
- public HeaderPriority DataRowPriority { get; set; }
- /// <summary>
- /// Gets or sets a value indicating that empty matrix should be printed.
- /// </summary>
- [Category("Behavior")]
- [DefaultValue(true)]
- public bool PrintIfEmpty { get; set; }
- /// <summary>
- /// Gets or sets a value indicating that the matrix should reset its data on each report run.
- /// </summary>
- /// <remarks>
- /// 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.
- /// </remarks>
- [Category("Behavior")]
- [DefaultValue(false)]
- public bool ResetDataOnRun { get; set; }
- /// <summary>
- /// Gets or sets a script method name that will be used to handle the
- /// <see cref="ManualBuild"/> event.
- /// </summary>
- /// <remarks>
- /// See the <see cref="ManualBuild"/> event for more details.
- /// </remarks>
- [Category("Build")]
- public string ManualBuildEvent { get; set; }
- /// <summary>
- /// Gets or sets a script method name that will be used to handle the
- /// <see cref="ModifyResult"/> event.
- /// </summary>
- /// <remarks>
- /// See the <see cref="ModifyResult"/> event for more details.
- /// </remarks>
- [Category("Build")]
- public string ModifyResultEvent { get; set; }
- /// <summary>
- /// Allows to fill the matrix in code.
- /// </summary>
- public event EventHandler ManualBuild;
- /// <summary>
- /// Allows to modify the prepared matrix elements such as cells, rows, columns.
- /// </summary>
- public event EventHandler ModifyResult;
- /// <summary>
- /// Gets the object that holds the collection of descriptors used to build a matrix.
- /// </summary>
- /// <remarks>
- /// See the <see cref="MatrixData"/> class for more details.
- /// </remarks>
- [Browsable(false)]
- public MatrixData Data { get; private set; }
- /// <summary>
- /// Gets or sets array of values that describes the currently printing column.
- /// </summary>
- /// <remarks>
- /// 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:
- /// <list type="bullet">
- /// <item>
- /// <description>select the cell that you need to highlight;</description>
- /// </item>
- /// <item>
- /// <description>click the "Highlight" button on the "Text" toolbar;</description>
- /// </item>
- /// <item>
- /// <description>add a new highlight condition. Use the <b>Matrix.ColumnValues</b> to
- /// refer to the value you need to analyze. Note: these values are array of <b>dynamic</b>,
- /// so you don't need to cast it to actual type before making any comparisons. Example of highlight
- /// condition: <c>Matrix1.ColumnValues[0] == 2000</c>.
- /// </description>
- /// </item>
- /// </list>
- /// </remarks>
- [Browsable(false)]
- public dynamic[] ColumnValues { get; set; }
- /// <summary>
- /// Gets or sets array of values that describes the currently printing row.
- /// </summary>
- /// <remarks>
- /// 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:
- /// <list type="bullet">
- /// <item>
- /// <description>select the cell that you need to highlight;</description>
- /// </item>
- /// <item>
- /// <description>click the "Highlight" button on the "Text" toolbar;</description>
- /// </item>
- /// <item>
- /// <description>add a new highlight condition. Use the <b>Matrix.RowValues</b> to
- /// refer to the value you need to analyze. Note: these values are arrays of <b>dynamic</b>,
- /// so you don't need to cast it to actual type before making any comparisons. Example of highlight
- /// condition: <c>Matrix1.RowValues[0] == "Andrew Fuller"</c>.
- /// </description>
- /// </item>
- /// </list>
- /// </remarks>
- [Browsable(false)]
- public dynamic[] RowValues { get; set; }
- /// <summary>
- /// Gets or sets the index of currently printing column.
- /// </summary>
- /// <remarks>
- /// This property may be used to print even columns with alternate color. To do this:
- /// <list type="bullet">
- /// <item>
- /// <description>select the cell that you need to highlight;</description>
- /// </item>
- /// <item>
- /// <description>click the "Highlight" button on the "Text" toolbar;</description>
- /// </item>
- /// <item>
- /// <description>add a new highlight condition that uses the <b>Matrix.ColumnIndex</b>,
- /// for example: <c>Matrix1.ColumnIndex % 2 == 1</c>.
- /// </description>
- /// </item>
- /// </list>
- /// </remarks>
- [Browsable(false)]
- public int ColumnIndex { get; set; }
- /// <summary>
- /// Gets or sets the index of currently printing row.
- /// </summary>
- /// <remarks>
- /// This property may be used to print even rows with alternate color. To do this:
- /// <list type="bullet">
- /// <item>
- /// <description>select the cell that you need to highlight;</description>
- /// </item>
- /// <item>
- /// <description>click the "Highlight" button on the "Text" toolbar;</description>
- /// </item>
- /// <item>
- /// <description>add a new highlight condition that uses the <b>Matrix.RowIndex</b>,
- /// for example: <c>Matrix1.RowIndex % 2 == 1</c>.
- /// </description>
- /// </item>
- /// </list>
- /// </remarks>
- [Browsable(false)]
- public int RowIndex { get; set; }
- /// <summary>
- /// Gets or sets the data row index of currently printing header.
- /// </summary>
- /// <remarks>
- /// 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
- /// </remarks>
- [Browsable(false)]
- public int RowNo { get; set; }
- /// <summary>
- /// Gets or sets count of items in the currently printing header.
- /// </summary>
- [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
- /// <inheritdoc/>
- 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
- /// <inheritdoc/>
- public override void Assign(Base source)
- {
- BaseAssign(source);
- AdvMatrixObject src = source as AdvMatrixObject;
- DataSource = src.DataSource;
- }
- /// <inheritdoc/>
- 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);
- }
- /// <summary>
- /// Creates or updates the matrix template.
- /// </summary>
- /// <remarks>
- /// Call this method after you modify the matrix descriptors using the <see cref="Data"/> object's properties.
- /// </remarks>
- 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);
- }
- /// <summary>
- /// This method fires the <b>ManualBuild</b> event and the script code connected to the <b>ManualBuildEvent</b>.
- /// </summary>
- /// <param name="e">Event data.</param>
- public void OnManualBuild(EventArgs e)
- {
- if (ManualBuild != null)
- ManualBuild(this, e);
- InvokeEvent(ManualBuildEvent, e);
- }
- /// <summary>
- /// This method fires the <b>ModifyResult</b> event and the script code connected to the <b>ModifyResultEvent</b>.
- /// </summary>
- /// <param name="e">Event data.</param>
- public void OnModifyResult(EventArgs e)
- {
- if (ModifyResult != null)
- ModifyResult(this, e);
- InvokeEvent(ModifyResultEvent, e);
- }
- /// <summary>
- /// Toggles visible state of the column with specified index. For internal use only.
- /// </summary>
- /// <param name="index">Index of column.</param>
- /// <param name="collapseAll">If true collapse all items.</param>
- /// <param name="expandAll">If true expand all items.</param>
- public void ToggleColumnVisible(int index, bool collapseAll = false, bool expandAll = false)
- {
- Data.Columns.Data.ToggleVisible(index, collapseAll, expandAll);
- }
- /// <summary>
- /// Toggles visible state of the row with specified index. For internal use only.
- /// </summary>
- /// <param name="index">Index of row.</param>
- /// <param name="collapseAll">If true collapse all items.</param>
- /// <param name="expandAll">If true expand all items.</param>
- public void ToggleRowVisible(int index, bool collapseAll = false, bool expandAll = false)
- {
- Data.Rows.Data.ToggleVisible(index, collapseAll, expandAll);
- }
- /// <summary>
- /// Sort columns based on values of row with specified index. For internal use only.
- /// </summary>
- /// <param name="rowIndex">Index of row.</param>
- /// <param name="sort">The sort order.</param>
- public void SortColumnsByRow(int rowIndex, SortOrder sort)
- {
- Data.InteractiveSort.Row.Index = rowIndex;
- Data.InteractiveSort.Row.Sort = sort;
- }
- /// <summary>
- /// Sort rows based on values of column with specified index. For internal use only.
- /// </summary>
- /// <param name="columnIndex">Index of column.</param>
- /// <param name="sort">The sort order.</param>
- public void SortRowsByColumn(int columnIndex, SortOrder sort)
- {
- Data.InteractiveSort.Column.Index = columnIndex;
- Data.InteractiveSort.Column.Sort = sort;
- }
- #endregion
- #region Report Engine
- /// <inheritdoc/>
- public override void ResetData()
- {
- Data.Reset();
- }
- /// <inheritdoc/>
- public override void InitializeComponent()
- {
- base.InitializeComponent();
- WireEvents(true);
- }
- /// <inheritdoc/>
- public override void FinalizeComponent()
- {
- base.FinalizeComponent();
- WireEvents(false);
- }
- /// <inheritdoc/>
- public override string[] GetExpressions()
- {
- List<string> expressions = new List<string>();
- TemplateBuilder.UpdateDescriptors(this);
- expressions.AddRange(base.GetExpressions());
- expressions.AddRange(Data.GetExpressions());
- if (!String.IsNullOrEmpty(Filter))
- expressions.Add(Filter);
- return expressions.ToArray();
- }
- /// <inheritdoc/>
- 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;
- }
- }
- /// <inheritdoc/>
- 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();
- }
- }
- /// <inheritdoc/>
- public override void OnAfterData(EventArgs e)
- {
- base.OnAfterData(e);
- Data.Done();
- if (PrintOnParent)
- ResultTable.AddToParent(Parent);
- }
- /// <inheritdoc/>
- 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
- /// <summary>
- /// Initializes a new instance of the <see cref="AdvMatrixObject"/> class.
- /// </summary>
- public AdvMatrixObject()
- {
- BaseName = "Matrix";
- Data = new MatrixData(this);
- Filter = "";
- PrintIfEmpty = true;
- StyleSheet = new Matrix.MatrixStyleSheet();
- StyleSheet.Load(ResourceLoader.GetStream("cross.frss"));
- style = "";
- ManualBuildEvent = "";
- ModifyResultEvent = "";
- }
- }
- }
|