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 = ""; } } }