| File: System\Windows\Documents\Table.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.ComponentModel; using System.Windows.Automation.Peers; using System.Windows.Markup; using MS.Internal.PtsHost.UnsafeNativeMethods; using MS.Internal.Documents; namespace System.Windows.Documents { /// <summary> /// Table implementation /// </summary> [ContentProperty("RowGroups")] public class Table : Block, IAddChild, IAcceptInsertion { //------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors /// <summary> /// Static ctor. Initializes property metadata. /// </summary> static Table() { MarginProperty.OverrideMetadata(typeof(Table), new FrameworkPropertyMetadata(new Thickness(Double.NaN))); } /// <summary> /// Table constructor. /// </summary> public Table() { PrivateInitialize(); } #endregion Constructors //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ #region Public Methods /// <summary> /// <see cref="IAddChild.AddChild"/> /// </summary> void IAddChild.AddChild(object value) { ArgumentNullException.ThrowIfNull(value); TableRowGroup rowGroup = value as TableRowGroup; if (rowGroup != null) { RowGroups.Add(rowGroup); return; } throw (new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(TableRowGroup)), nameof(value))); } /// <summary> /// <see cref="IAddChild.AddText"/> /// </summary> void IAddChild.AddText(string text) { XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this); } /// <summary> /// Initialization of this element is about to begin /// </summary> public override void BeginInit() { base.BeginInit(); _initializing = true; } /// <summary> /// Initialization of this element has completed /// </summary> public override void EndInit() { _initializing = false; OnStructureChanged(); base.EndInit(); } /// <summary> /// <see cref="FrameworkContentElement.LogicalChildren"/> /// </summary> /// <remarks> /// children enumerated in the following order: /// * columns /// * rowgroups /// </remarks> protected internal override IEnumerator LogicalChildren { get { return (new TableChildrenCollectionEnumeratorSimple(this)); } } #endregion Public Methods //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ #region Public Properties /// <summary> /// Returns table column collection. /// </summary> [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public TableColumnCollection Columns { get { return (_columns); } } /// <summary> /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// </summary> [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeColumns() { return (Columns.Count > 0); } /// <summary> /// Returns table row group. /// </summary> [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public TableRowGroupCollection RowGroups { get { return (_rowGroups); } } /// <summary> /// Cell spacing property. /// </summary> [TypeConverter(typeof(LengthConverter))] public double CellSpacing { get { return (double) GetValue(CellSpacingProperty); } set { SetValue(CellSpacingProperty, value); } } #endregion Public Properties //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ #region Protected Methods /// <summary> /// Creates AutomationPeer (<see cref="ContentElement.OnCreateAutomationPeer"/>) /// </summary> protected override AutomationPeer OnCreateAutomationPeer() { return new TableAutomationPeer(this); } #endregion Protected Methods //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ #region Internal Properties /// <summary> /// Internal cell spacing getter /// </summary> internal double InternalCellSpacing { get { return Math.Max(CellSpacing, 0); } } int IAcceptInsertion.InsertionIndex { get { return this.InsertionIndex; } set { this.InsertionIndex = value; } } /// <summary> /// Stores temporary data for where to insert a new row group /// </summary> internal int InsertionIndex { get { return _rowGroupInsertionIndex; } set { _rowGroupInsertionIndex = value; } } /// <summary> /// Count of columns in the table /// </summary> internal int ColumnCount { get { return (_columnCount); } } #endregion Internal Properties //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods /// <summary> /// Updates table actual column count /// </summary> /// <param name="columnCount">Count of column to account for</param> internal void EnsureColumnCount(int columnCount) { if (_columnCount < columnCount) _columnCount = columnCount; } /// <summary> /// OnStructureChanged - Called to rebuild structure. /// </summary> internal void OnStructureChanged() { if (!_initializing) { if (TableStructureChanged != null) { TableStructureChanged(this, EventArgs.Empty); } ValidateStructure(); // Table structure changes affect number of rows and colums. Need to notify peer about it. TableAutomationPeer peer = ContentElementAutomationPeer.FromElement(this) as TableAutomationPeer; peer?.OnStructureInvalidated(); } } /// <summary> /// ValidateStructure /// </summary> internal void ValidateStructure() { if (!_initializing) { // // validate row groups structural cache // _columnCount = 0; for (int i = 0; i < _rowGroups.Count; ++i) { _rowGroups[i].ValidateStructure(); } _version++; } } /// <summary> /// Notifies the text container that some property change has occurred, requiring a revalidation of table. /// </summary> internal void InvalidateColumns() { NotifyTypographicPropertyChanged(true /* affectsMeasureOrArrange */, true /* localValueChanged */, null); } /// <summary> /// Returns true if the given rowGroupIndex is the first non-empty one /// </summary> internal bool IsFirstNonEmptyRowGroup(int rowGroupIndex) { rowGroupIndex--; while(rowGroupIndex >= 0) { if(RowGroups[rowGroupIndex].Rows.Count > 0) { return false; } rowGroupIndex--; } return true; } /// <summary> /// Returns true if the given rowGroupIndex is the last non-empty one /// </summary> internal bool IsLastNonEmptyRowGroup(int rowGroupIndex) { rowGroupIndex++; while(rowGroupIndex < RowGroups.Count) { if(RowGroups[rowGroupIndex].Rows.Count > 0) { return false; } rowGroupIndex++; } return true; } #endregion Internal Methods //------------------------------------------------------ // // Internal Events // //------------------------------------------------------ #region Internal Events /// <summary> /// Fired when the table changes structurally /// </summary> internal event EventHandler TableStructureChanged; #endregion //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods /// <summary> /// Private ctor time initialization. /// </summary> private void PrivateInitialize() { // Acquire new PTS Context. _columns = new TableColumnCollection(this); _rowGroups = new TableRowGroupCollection(this); _rowGroupInsertionIndex = -1; } private static bool IsValidCellSpacing(object o) { double spacing = (double)o; double maxSpacing = Math.Min(1000000, PTS.MaxPageSize); if (Double.IsNaN(spacing)) { return false; } if (spacing < 0 || spacing > maxSpacing) { return false; } return true; } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ #region Private Fields private TableColumnCollection _columns; // collection of columns private TableRowGroupCollection _rowGroups; // collection of row groups private int _rowGroupInsertionIndex; // insertion index used by row group collection private const double c_defaultCellSpacing = 2; // default value of cell spacing private int _columnCount; private int _version = 0; private bool _initializing; // True if the table is being initialized #endregion Private Fields //------------------------------------------------------ // // Private Structures / Classes // //------------------------------------------------------ #region Private Structures Classes /// <summary> /// Implementation of a simple enumerator of table's children /// </summary> private class TableChildrenCollectionEnumeratorSimple : IEnumerator, ICloneable { internal TableChildrenCollectionEnumeratorSimple(Table table) { Debug.Assert(table != null); _table = table; _version = _table._version; _columns = ((IEnumerable)_table._columns).GetEnumerator(); _rowGroups = ((IEnumerable)_table._rowGroups).GetEnumerator(); } public Object Clone() { return (MemberwiseClone()); } public bool MoveNext() { if (_version != _table._version) { throw new InvalidOperationException(SR.EnumeratorVersionChanged); } // Strange design, but iterator must spin on contained column iterator if ((_currentChildType != ChildrenTypes.Columns) && (_currentChildType != ChildrenTypes.RowGroups)) _currentChildType++; Object currentChild = null; while (_currentChildType < ChildrenTypes.AfterLast) { switch (_currentChildType) { case (ChildrenTypes.Columns): if (_columns.MoveNext()) { currentChild = _columns.Current; } break; case (ChildrenTypes.RowGroups): if (_rowGroups.MoveNext()) { currentChild = _rowGroups.Current; } break; } if (currentChild != null) { _currentChild = currentChild; break; } _currentChildType++; } Debug.Assert(_currentChildType != ChildrenTypes.BeforeFirst); return (_currentChildType != ChildrenTypes.AfterLast); } public Object Current { get { if (_currentChildType == ChildrenTypes.BeforeFirst) { // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.EnumeratorNotStarted); } if (_currentChildType == ChildrenTypes.AfterLast) { // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.EnumeratorReachedEnd); } return (_currentChild); } } public void Reset() { if (_version != _table._version) { throw new InvalidOperationException(SR.EnumeratorVersionChanged); } _columns.Reset(); _rowGroups.Reset(); _currentChildType = ChildrenTypes.BeforeFirst; _currentChild = null; } private Table _table; private int _version; private IEnumerator _columns; private IEnumerator _rowGroups; private ChildrenTypes _currentChildType; private Object _currentChild; private enum ChildrenTypes : int { BeforeFirst = 0, Columns = 1, RowGroups = 2, AfterLast = 3, } } #endregion Private Structures Classes //------------------------------------------------------ // // Properties // //------------------------------------------------------ #region Properties /// <summary> /// Cell spacing property. /// </summary> public static readonly DependencyProperty CellSpacingProperty = DependencyProperty.Register( "CellSpacing", typeof(double), typeof(Table), new FrameworkPropertyMetadata( c_defaultCellSpacing, FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(IsValidCellSpacing)); #endregion Properties //------------------------------------------------------ // // Debug / Performance // //------------------------------------------------------ #if TABLEPARANOIA internal int ParanoiaVersion = 0; #endif // TABLEPARANOIA } }