using System; using System.Collections.Generic; using System.ComponentModel; using FastReport.Utils; using FastReport.Data; using System.Drawing.Design; namespace FastReport { /// /// This class represents the Data band. /// /// /// Use the property to connect the band to a datasource. Set the /// property if you want to filter data rows. The /// property can be used to sort data rows. /// public partial class DataBand : BandBase { #region Fields private DataHeaderBand header; private DataFooterBand footer; private BandCollection bands; private DataSourceBase dataSource; private SortCollection sort; private string filter; private BandColumns columns; private bool printIfDetailEmpty; private bool printIfDatasourceEmpty; private bool keepTogether; private bool keepDetail; private string idColumn; private string parentIdColumn; private float indent; private bool keepSummary; private Relation relation; private bool collectChildRows; private int rowCount; private int maxRows; private bool resetPageNumber; #endregion #region Properties /// /// Gets or sets a header band. /// [Browsable(false)] public DataHeaderBand Header { get { return header; } set { SetProp(header, value); header = value; } } /// /// Gets a collection of detail bands. /// [Browsable(false)] public BandCollection Bands { get { return bands; } } /// /// Gets or sets a footer band. /// [Browsable(false)] public DataFooterBand Footer { get { return footer; } set { SetProp(footer, value); footer = value; } } /// /// Gets or sets a data source. /// Please note: data source have to be enabled. /// [Category("Data")] public DataSourceBase DataSource { get { if (dataSource != null && !dataSource.Enabled) return null; return dataSource; } set { if (dataSource != value) { if (dataSource != null) dataSource.Disposed -= new EventHandler(DataSource_Disposed); if (value != null) value.Disposed += new EventHandler(DataSource_Disposed); } dataSource = value; } } /// /// Gets or sets a number of rows in the virtual data source. /// /// /// Use this property if your data band is not connected to any data source. In this case /// the virtual data source with the specified number of rows will be used. /// [Category("Data")] [DefaultValue(1)] public int RowCount { get { return rowCount; } set { rowCount = value; } } /// /// Limits the maximum number of rows in a datasource. 0 means no limit. /// [Category("Data")] [DefaultValue(0)] public int MaxRows { get { return maxRows; } set { maxRows = value; } } /// /// Gets or sets a relation used to establish a master-detail relationship between /// this band and its parent. /// /// /// Use this property if there are several relations exist between two data sources. /// If there is only one relation (in most cases it is), you can leave this property empty. /// [Category("Data")] [Editor("FastReport.TypeEditors.RelationEditor, FastReport", typeof(UITypeEditor))] public Relation Relation { get { return relation; } set { relation = value; } } /// /// Gets the collection of sort conditions. /// [Browsable(false)] public SortCollection Sort { get { return sort; } } /// /// Gets the row filter expression. /// /// /// This property can contain any valid boolean expression. If the expression returns false, /// the corresponding data row will not be printed. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string Filter { get { return filter; } set { filter = value; } } /// /// Gets the band columns. /// [Category("Appearance")] [Editor("FastReport.TypeEditors.DataBandColumnEditor, FastReport", typeof(UITypeEditor))] public BandColumns Columns { get { return columns; } } /// /// Gets or sets a value that determines whether to print a band if all its detail rows are empty. /// [DefaultValue(false)] [Category("Behavior")] public bool PrintIfDetailEmpty { get { return printIfDetailEmpty; } set { printIfDetailEmpty = value; } } /// /// Gets or sets a value that determines whether to print a band if its datasource is empty. /// [DefaultValue(false)] [Category("Behavior")] public bool PrintIfDatasourceEmpty { get { return printIfDatasourceEmpty; } set { printIfDatasourceEmpty = value; } } /// /// Gets or sets a value indicating that all band rows should be printed together on one page. /// [DefaultValue(false)] [Category("Behavior")] public bool KeepTogether { get { return keepTogether; } set { keepTogether = value; } } /// /// Gets or sets a value indicating that the band should be printed together with all its detail rows. /// [DefaultValue(false)] [Category("Behavior")] public bool KeepDetail { get { return keepDetail; } set { keepDetail = value; } } /// /// Gets or sets the key column that identifies the data row. /// /// /// This property is used when printing a hierarchic list. /// To print the hierarchic list, you have to setup three properties: IdColumn, /// ParentIdColumn and Indent. First two properties are used to identify the data /// row and its parent; the Indent property specifies the indent that will be used to shift /// the databand according to its hierarchy level. /// When printing hierarchy, FastReport shifts the band to the right /// (by value specified in the property), and also decreases the /// width of the band by the same value. You may use the Anchor property of the /// objects on a band to indicate whether the object should move with the band, or stay /// on its original position, or shrink. /// [Category("Hierarchy")] [Editor("FastReport.TypeEditors.DataColumnEditor, FastReport", typeof(UITypeEditor))] public string IdColumn { get { return idColumn; } set { idColumn = value; } } /// /// Gets or sets the column that identifies the parent data row. /// /// /// This property is used when printing a hierarchic list. See description of the /// property for more details. /// [Category("Hierarchy")] [Editor("FastReport.TypeEditors.DataColumnEditor, FastReport", typeof(UITypeEditor))] public string ParentIdColumn { get { return parentIdColumn; } set { parentIdColumn = value; } } /// /// Gets or sets the indent that will be used to shift the databand according to its hierarchy level. /// /// /// This property is used when printing a hierarchic list. See description of the /// property for more details. /// [DefaultValue(37.8f)] [Category("Hierarchy")] [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")] public float Indent { get { return indent; } set { indent = value; } } /// /// Gets or sets a value indicating that the databand should collect child data rows. /// /// /// This property determines how the master-detail report is printed. Default behavior is: /// MasterData row1 /// -- DetailData row1 /// -- DetailData row2 /// -- DetailData row3 /// MasterData row2 /// -- DetailData row1 /// -- DetailData row2 /// When you set this property to true, the master databand will collect all child data rows /// under a single master data row: /// MasterData row1 /// -- DetailData row1 /// -- DetailData row2 /// -- DetailData row3 /// -- DetailData row4 /// -- DetailData row5 /// [DefaultValue(false)] [Category("Behavior")] public bool CollectChildRows { get { return collectChildRows; } set { collectChildRows = value; } } /// /// Gets or sets a value that determines whether to reset the page numbers when this band starts print. /// /// /// Typically you should set the property to true as well. /// [DefaultValue(false)] [Category("Behavior")] public bool ResetPageNumber { get { return resetPageNumber; } set { resetPageNumber = value; } } internal bool IsDeepmostDataBand { get { return Bands.Count == 0; } } internal bool KeepSummary { get { return keepSummary; } set { keepSummary = value; } } internal bool IsHierarchical { get { return !String.IsNullOrEmpty(IdColumn) && !String.IsNullOrEmpty(ParentIdColumn); } } internal bool IsDatasourceEmpty { get { return DataSource == null || DataSource.RowCount == 0; } } #endregion #region Private Methods private void DataSource_Disposed(object sender, EventArgs e) { dataSource = null; } #endregion #region Protected Methods /// protected override void DeserializeSubItems(FRReader reader) { if (String.Compare(reader.ItemName, "Sort", true) == 0) reader.Read(Sort); else base.DeserializeSubItems(reader); } #endregion #region IParent /// public override void GetChildObjects(ObjectCollection list) { base.GetChildObjects(list); if (IsRunning) return; list.Add(header); foreach (BandBase band in bands) { list.Add(band); } list.Add(footer); } /// public override bool CanContain(Base child) { return base.CanContain(child) || (child is DataHeaderBand || child is DataFooterBand || child is DataBand || child is GroupHeaderBand); } /// public override void AddChild(Base child) { if (IsRunning) { base.AddChild(child); return; } if (child is DataHeaderBand) Header = child as DataHeaderBand; else if (child is DataFooterBand) Footer = child as DataFooterBand; else if (child is DataBand || child is GroupHeaderBand) bands.Add(child as BandBase); else base.AddChild(child); } /// public override void RemoveChild(Base child) { base.RemoveChild(child); if (IsRunning) return; if (child is DataHeaderBand && header == child as DataHeaderBand) Header = null; if (child is DataFooterBand && footer == child as DataFooterBand) Footer = null; if (child is DataBand || child is GroupHeaderBand) bands.Remove(child as BandBase); } /// public override int GetChildOrder(Base child) { if (child is BandBase && !IsRunning) return bands.IndexOf(child as BandBase); return base.GetChildOrder(child); } /// public override void SetChildOrder(Base child, int order) { if (child is BandBase && !IsRunning) { if (order > bands.Count) order = bands.Count; int oldOrder = child.ZOrder; if (oldOrder != -1 && order != -1 && oldOrder != order) { if (oldOrder <= order) order--; bands.Remove(child as BandBase); bands.Insert(order, child as BandBase); } } else base.SetChildOrder(child, order); } #endregion #region Public Methods /// public override void Assign(Base source) { base.Assign(source); DataBand src = source as DataBand; DataSource = src.DataSource; RowCount = src.RowCount; MaxRows = src.MaxRows; Relation = src.Relation; Sort.Assign(src.Sort); Filter = src.Filter; Columns.Assign(src.Columns); PrintIfDetailEmpty = src.PrintIfDetailEmpty; PrintIfDatasourceEmpty = src.PrintIfDatasourceEmpty; KeepTogether = src.KeepTogether; KeepDetail = src.KeepDetail; IdColumn = src.IdColumn; ParentIdColumn = src.ParentIdColumn; Indent = src.Indent; CollectChildRows = src.CollectChildRows; ResetPageNumber = src.ResetPageNumber; } internal override void UpdateWidth() { if (Columns.Count > 1) { Width = Columns.ActualWidth; } else if (!String.IsNullOrEmpty(IdColumn) && !String.IsNullOrEmpty(ParentIdColumn)) { if (PageWidth != 0) Width = PageWidth - Left; } else base.UpdateWidth(); } /// public override void Serialize(FRWriter writer) { DataBand c = writer.DiffObject as DataBand; base.Serialize(writer); if (writer.SerializeTo == SerializeTo.Preview) return; if (DataSource != c.DataSource) writer.WriteRef("DataSource", DataSource); if (RowCount != c.RowCount) writer.WriteInt("RowCount", RowCount); if (MaxRows != c.MaxRows) writer.WriteInt("MaxRows", MaxRows); if (Relation != c.Relation) writer.WriteRef("Relation", Relation); if (Sort.Count > 0) writer.Write(Sort); if (Filter != c.Filter) writer.WriteStr("Filter", Filter); Columns.Serialize(writer, c.Columns); if (PrintIfDetailEmpty != c.PrintIfDetailEmpty) writer.WriteBool("PrintIfDetailEmpty", PrintIfDetailEmpty); if (PrintIfDatasourceEmpty != c.PrintIfDatasourceEmpty) writer.WriteBool("PrintIfDatasourceEmpty", PrintIfDatasourceEmpty); if (KeepTogether != c.KeepTogether) writer.WriteBool("KeepTogether", KeepTogether); if (KeepDetail != c.KeepDetail) writer.WriteBool("KeepDetail", KeepDetail); if (IdColumn != c.IdColumn) writer.WriteStr("IdColumn", IdColumn); if (ParentIdColumn != c.ParentIdColumn) writer.WriteStr("ParentIdColumn", ParentIdColumn); if (FloatDiff(Indent, c.Indent)) writer.WriteFloat("Indent", Indent); if (CollectChildRows != c.CollectChildRows) writer.WriteBool("CollectChildRows", CollectChildRows); if (ResetPageNumber != c.ResetPageNumber) writer.WriteBool("ResetPageNumber", ResetPageNumber); } /// public override string[] GetExpressions() { List list = new List(); foreach (Sort sort in Sort) { list.Add(sort.Expression); } list.Add(Filter); return list.ToArray(); } /// /// Initializes the data source connected to this band. /// public void InitDataSource() { if (DataSource == null) { DataSource = new VirtualDataSource(); DataSource.SetReport(Report); } if (DataSource is VirtualDataSource) (DataSource as VirtualDataSource).VirtualRowsCount = RowCount; DataSourceBase parentDataSource = ParentDataBand == null ? null : ParentDataBand.DataSource; bool collectChildRows = ParentDataBand == null ? false : ParentDataBand.CollectChildRows; if (Relation != null) DataSource.Init(Relation, Filter, Sort, collectChildRows); else DataSource.Init(parentDataSource, Filter, Sort, collectChildRows); } internal bool IsDetailEmpty() { if (PrintIfDetailEmpty || Bands.Count == 0) return false; foreach (BandBase band in Bands) { if (!band.IsEmpty()) return false; } return true; } internal override bool IsEmpty() { InitDataSource(); if (IsDatasourceEmpty) return !PrintIfDatasourceEmpty; DataSource.First(); while (DataSource.HasMoreRows) { if (!IsDetailEmpty()) return false; DataSource.Next(); } return true; } /// public override void InitializeComponent() { base.InitializeComponent(); KeepSummary = false; } #endregion /// /// Initializes a new instance of the class. /// public DataBand() { bands = new BandCollection(this); sort = new SortCollection(); filter = ""; columns = new BandColumns(this); idColumn = ""; parentIdColumn = ""; indent = 37.8f; rowCount = 1; SetFlags(Flags.HasSmartTag, true); } } }