| File: System\Windows\Documents\TableRow.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. // // Description: Table row implementation // // See spec at WPP TableOM.doc // using MS.Internal.PtsTable; using System.ComponentModel; using System.Windows.Markup; using MS.Internal.Documents; namespace System.Windows.Documents { /// <summary> /// Table row /// </summary> [ContentProperty("Cells")] public class TableRow : TextElement, IAddChild, IIndexedChild<TableRowGroup>, IAcceptInsertion { //------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors /// <summary> /// Creates an instance of a Row /// </summary> public TableRow() : base() { PrivateInitialize(); } #endregion //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ #region Public Methods /// <summary> /// <see cref="IAddChild.AddChild"/> /// </summary> void IAddChild.AddChild(object value) { ArgumentNullException.ThrowIfNull(value); TableCell cell = value as TableCell; if (cell != null) { Cells.Add(cell); return; } throw (new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(TableCell)), nameof(value))); } /// <summary> /// <see cref="IAddChild.AddText"/> /// </summary> void IAddChild.AddText(string text) { XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this); } /// <summary> /// Called when tablerow gets new parent /// </summary> /// <param name="newParent"> /// New parent of cell /// </param> internal override void OnNewParent(DependencyObject newParent) { DependencyObject oldParent = this.Parent; if (newParent != null && !(newParent is TableRowGroup)) { throw new InvalidOperationException(SR.Format(SR.TableInvalidParentNodeType, newParent.GetType().ToString())); } if (oldParent != null) { ((TableRowGroup)oldParent).Rows.InternalRemove(this); } base.OnNewParent(newParent); if (newParent != null) { ((TableRowGroup)newParent).Rows.InternalAdd(this); } } #endregion Public Methods //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ #region Protected Methods #endregion Protected Methods //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods #region IIndexedChild implementation /// <summary> /// Callback used to notify about entering model tree. /// </summary> void IIndexedChild<TableRowGroup>.OnEnterParentTree() { this.OnEnterParentTree(); } /// <summary> /// Callback used to notify about exitting model tree. /// </summary> void IIndexedChild<TableRowGroup>.OnExitParentTree() { this.OnExitParentTree(); } void IIndexedChild<TableRowGroup>.OnAfterExitParentTree(TableRowGroup parent) { this.OnAfterExitParentTree(parent); } int IIndexedChild<TableRowGroup>.Index { get { return this.Index; } set { this.Index = value; } } #endregion IIndexedChild implementation /// <summary> /// Callback used to notify the Row about entering model tree. /// </summary> internal void OnEnterParentTree() { Table?.OnStructureChanged(); } /// <summary> /// Callback used to notify the RowGroup about exitting model tree. /// </summary> internal void OnExitParentTree() { } /// <summary> /// Callback used to notify the Row about exitting model tree. /// </summary> internal void OnAfterExitParentTree(TableRowGroup rowGroup) { if (rowGroup.Table != null) { Table.OnStructureChanged(); } } /// <summary> /// ValidateStructure /// </summary> internal void ValidateStructure(RowSpanVector rowSpanVector) { Debug.Assert(rowSpanVector != null); SetFlags(!rowSpanVector.Empty(), Flags.HasForeignCells); SetFlags(false, Flags.HasRealCells); _formatCellCount = 0; _columnCount = 0; int firstAvailableIndex; int firstOccupiedIndex; rowSpanVector.GetFirstAvailableRange(out firstAvailableIndex, out firstOccupiedIndex); for (int i = 0; i < _cells.Count; ++i) { TableCell cell = _cells[i]; // Get cloumn span and row span. Row span is limited to the number of rows in the row group. // Since we do not know the number of columns in the table at this point, column span is limited only // by internal constants int columnSpan = cell.ColumnSpan; int rowSpan = cell.RowSpan; while (firstAvailableIndex + columnSpan > firstOccupiedIndex) { rowSpanVector.GetNextAvailableRange(out firstAvailableIndex, out firstOccupiedIndex); } Debug.Assert(i <= firstAvailableIndex); cell.ValidateStructure(firstAvailableIndex); if (rowSpan > 1) { rowSpanVector.Register(cell); } else { _formatCellCount++; } firstAvailableIndex += columnSpan; } _columnCount = firstAvailableIndex; bool isLastRowOfAnySpan = false; rowSpanVector.GetSpanCells(out _spannedCells, out isLastRowOfAnySpan); Debug.Assert(_spannedCells != null); if ((_formatCellCount > 0) || isLastRowOfAnySpan) { SetFlags(true, Flags.HasRealCells); } _formatCellCount += _spannedCells.Length; Debug.Assert(_cells.Count <= _formatCellCount); } #endregion Internal Methods //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ #region Internal Properties /// <summary> /// RowGroup owner accessor /// </summary> internal TableRowGroup RowGroup { get { return (Parent as TableRowGroup); } } /// <summary> /// Table owner accessor /// </summary> internal Table Table { get { return (RowGroup?.Table); } } /// <summary> /// Returns the row's cell collection /// </summary> [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public TableCellCollection Cells { get { return (_cells); } } /// <summary> /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// </summary> [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeCells() { return (Cells.Count > 0); } /// <summary> /// Row's index in the parents collection. /// </summary> internal int Index { get { return (_parentIndex); } set { Debug.Assert(value >= -1 && _parentIndex != value); _parentIndex = value; } } int IAcceptInsertion.InsertionIndex { get { return this.InsertionIndex; } set { this.InsertionIndex = value; } } /// <summary> /// Stores temporary data for where to insert a new cell /// </summary> internal int InsertionIndex { get { return _cellInsertionIndex; } set { _cellInsertionIndex = value; } } /// <summary> /// Returns span cells vector /// </summary> internal TableCell[] SpannedCells { get { return (_spannedCells); } } /// <summary> /// Count of columns in the table /// </summary> internal int ColumnCount { get { return (_columnCount); } } /// <summary> /// Returns "true" if there are row spanned cells belonging to previous rows /// </summary> internal bool HasForeignCells { get { return (CheckFlags(Flags.HasForeignCells)); } } /// <summary> /// Returns "true" if there are row spanned cells belonging to previous rows /// </summary> internal bool HasRealCells { get { return (CheckFlags(Flags.HasRealCells)); } } /// <summary> /// Count of columns in the table /// </summary> internal int FormatCellCount { get { return (_formatCellCount); } } /// <summary> /// Marks this element's left edge as visible to IMEs. /// This means element boundaries will act as word breaks. /// </summary> internal override bool IsIMEStructuralElement { get { return true; } } #endregion Internal Properties //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods /// <summary> /// Private ctor time initialization. /// </summary> private void PrivateInitialize() { _cells = new TableCellCollection(this); _parentIndex = -1; _cellInsertionIndex = -1; } /// <summary> /// SetFlags is used to set or unset one or multiple flags on the row. /// </summary> private void SetFlags(bool value, Flags flags) { _flags = value ? (_flags | flags) : (_flags & (~flags)); } /// <summary> /// CheckFlags returns true if all flags in the bitmask flags are set. /// </summary> private bool CheckFlags(Flags flags) { return ((_flags & flags) == flags); } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ #region Private Fields private TableCellCollection _cells; // collection of cells belonging to the row private TableCell[] _spannedCells; // row spanned cell storage private int _parentIndex; // row's index in parent's children collection private int _cellInsertionIndex; // Insertion index a cell private int _columnCount; private Flags _flags; // flags reflecting various aspects of row's state private int _formatCellCount; // count of the cell to be formatted in this row #endregion Private Fields //------------------------------------------------------ // // Private Structures / Classes // //------------------------------------------------------ #region Private Structures Classes [System.Flags] private enum Flags { // // state flags // HasForeignCells = 0x00000010, // there are overhanging cells from the previous rows HasRealCells = 0x00000020, // real cells in row (not just spanning) (Only known by validation, not format) } #endregion Private Structures Classes } }