Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / Grid.cs / 1 / Grid.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
//
// Description: Grid implementation.
//
// Specs
// Grid : [....]/layout/Specs/Grid.mht
// Size Sharing: [....]/layout/Specs/Size%20Information%20Sharing.doc
//
// Misc
// Grid Tutorial: [....]/layout/Documentation%20and%20Tutorials/Grid%20Tutorial.mht
//
// History:
// 09/24/2003 : [....] - Created (in griddy prototype branch);
// 10/27/2003 : [....] - Ported from griddy prototype branch;
// 01/20/2004 : [....] - Switching to use UIElementCollection;
//
//
using MS.Internal;
using MS.Internal.Controls;
using MS.Internal.PresentationFramework;
using MS.Utility;
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Threading;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Markup;
#pragma warning disable 1634, 1691 // suppressing PreSharp warnings
namespace System.Windows.Controls
{
///
/// Grid
///
public class Grid : Panel, IAddChild
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
///
/// Default constructor.
///
public Grid()
{
SetFlags((bool) ShowGridLinesProperty.GetDefaultValue(DependencyObjectType), Flags.ShowGridLinesPropertyValue);
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
///
///
void IAddChild.AddChild(object value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
UIElement cell = value as UIElement;
if (cell != null)
{
Children.Add(cell);
return;
}
throw (new ArgumentException(SR.Get(SRID.Grid_UnexpectedParameterType, value.GetType(), typeof(UIElement)), "value"));
}
///
///
///
void IAddChild.AddText(string text)
{
XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
}
///
///
///
protected internal override IEnumerator LogicalChildren
{
get
{
// empty panel or a panel being used as the items
// host has *no* logical children; give empty enumerator
bool noChildren = (base.VisualChildrenCount == 0) || IsItemsHost;
if (noChildren)
{
ExtendedData extData = ExtData;
if ( extData == null
|| ( (extData.ColumnDefinitions == null || extData.ColumnDefinitions.Count == 0)
&& (extData.RowDefinitions == null || extData.RowDefinitions.Count == 0) )
)
{
// grid is empty
return EmptyEnumerator.Instance;
}
}
return (new GridChildrenCollectionEnumeratorSimple(this, !noChildren));
}
}
///
/// Helper for setting Column property on a UIElement.
///
/// UIElement to set Column property on.
/// Column property value.
public static void SetColumn(UIElement element, int value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(ColumnProperty, value);
}
///
/// Helper for reading Column property from a UIElement.
///
/// UIElement to read Column property from.
/// Column property value.
[AttachedPropertyBrowsableForChildren()]
public static int GetColumn(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return ((int)element.GetValue(ColumnProperty));
}
///
/// Helper for setting Row property on a UIElement.
///
/// UIElement to set Row property on.
/// Row property value.
public static void SetRow(UIElement element, int value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(RowProperty, value);
}
///
/// Helper for reading Row property from a UIElement.
///
/// UIElement to read Row property from.
/// Row property value.
[AttachedPropertyBrowsableForChildren()]
public static int GetRow(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return ((int)element.GetValue(RowProperty));
}
///
/// Helper for setting ColumnSpan property on a UIElement.
///
/// UIElement to set ColumnSpan property on.
/// ColumnSpan property value.
public static void SetColumnSpan(UIElement element, int value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(ColumnSpanProperty, value);
}
///
/// Helper for reading ColumnSpan property from a UIElement.
///
/// UIElement to read ColumnSpan property from.
/// ColumnSpan property value.
[AttachedPropertyBrowsableForChildren()]
public static int GetColumnSpan(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return ((int)element.GetValue(ColumnSpanProperty));
}
///
/// Helper for setting RowSpan property on a UIElement.
///
/// UIElement to set RowSpan property on.
/// RowSpan property value.
public static void SetRowSpan(UIElement element, int value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(RowSpanProperty, value);
}
///
/// Helper for reading RowSpan property from a UIElement.
///
/// UIElement to read RowSpan property from.
/// RowSpan property value.
[AttachedPropertyBrowsableForChildren()]
public static int GetRowSpan(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return ((int)element.GetValue(RowSpanProperty));
}
///
/// Helper for setting IsSharedSizeScope property on a UIElement.
///
/// UIElement to set IsSharedSizeScope property on.
/// IsSharedSizeScope property value.
public static void SetIsSharedSizeScope(UIElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsSharedSizeScopeProperty, value);
}
///
/// Helper for reading IsSharedSizeScope property from a UIElement.
///
/// UIElement to read IsSharedSizeScope property from.
/// IsSharedSizeScope property value.
public static bool GetIsSharedSizeScope(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return ((bool)element.GetValue(IsSharedSizeScopeProperty));
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region Public Properties
///
/// ShowGridLines property.
///
public bool ShowGridLines
{
get { return (CheckFlagsAnd(Flags.ShowGridLinesPropertyValue)); }
set { SetValue(ShowGridLinesProperty, value); }
}
///
/// Returns a ColumnDefinitionCollection of column definitions.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ColumnDefinitionCollection ColumnDefinitions
{
get
{
if (_data == null) { _data = new ExtendedData(); }
if (_data.ColumnDefinitions == null) { _data.ColumnDefinitions = new ColumnDefinitionCollection(this); }
return (_data.ColumnDefinitions);
}
}
///
/// Returns a RowDefinitionCollection of row definitions.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public RowDefinitionCollection RowDefinitions
{
get
{
if (_data == null) { _data = new ExtendedData(); }
if (_data.RowDefinitions == null) { _data.RowDefinitions = new RowDefinitionCollection(this); }
return (_data.RowDefinitions);
}
}
#endregion Public Properties
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Derived class must implement to support Visual children. The method must return
/// the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1.
///
/// By default a Visual does not have any children.
///
/// Remark:
/// During this virtual call it is not valid to modify the Visual tree.
///
protected override Visual GetVisualChild(int index)
{
// because "base.Count + 1" for GridLinesRenderer
// argument checking done at the base class
if(index == base.VisualChildrenCount)
{
if (_gridLinesRenderer == null)
{
throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange));
}
return _gridLinesRenderer;
}
else return base.GetVisualChild(index);
}
///
/// Derived classes override this property to enable the Visual code to enumerate
/// the Visual children. Derived classes need to return the number of children
/// from this method.
///
/// By default a Visual does not have any children.
///
/// Remark: During this virtual method the Visual tree must not be modified.
///
protected override int VisualChildrenCount
{
//since GridLinesRenderer has not been added as a child, so we do not subtract
get { return base.VisualChildrenCount + (_gridLinesRenderer != null ? 1 : 0); }
}
///
/// Content measurement.
///
/// Constraint
/// Desired size
protected override Size MeasureOverride(Size constraint)
{
Size gridDesiredSize;
ExtendedData extData = ExtData;
try
{
EnterCounterScope(Counters.MeasureOverride);
ListenToNotifications = true;
MeasureOverrideInProgress = true;
if (extData == null)
{
gridDesiredSize = new Size();
UIElementCollection children = InternalChildren;
for (int i = 0, count = children.Count; i < count; ++i)
{
UIElement child = children[i];
if (child != null)
{
child.Measure(constraint);
gridDesiredSize.Width = Math.Max(gridDesiredSize.Width, child.DesiredSize.Width);
gridDesiredSize.Height = Math.Max(gridDesiredSize.Height, child.DesiredSize.Height);
}
}
}
else
{
{
bool sizeToContentU = double.IsPositiveInfinity(constraint.Width);
bool sizeToContentV = double.IsPositiveInfinity(constraint.Height);
ValidateDefinitionsUStructure();
ValidateDefinitionsLayout(DefinitionsU, sizeToContentU);
ValidateDefinitionsVStructure();
ValidateDefinitionsLayout(DefinitionsV, sizeToContentV);
CellsStructureDirty |= (SizeToContentU != sizeToContentU) || (SizeToContentV != sizeToContentV);
SizeToContentU = sizeToContentU;
SizeToContentV = sizeToContentV;
}
ValidateCells();
Debug.Assert(DefinitionsU.Length > 0 && DefinitionsV.Length > 0);
// Grid classifies cells into four groups depending on
// the column / row type a cell belongs to (number corresponds to
// group number):
//
// Px Auto Star
// +--------+--------+--------+
// | | | |
// Px | 1 | 1 | 3 |
// | | | |
// +--------+--------+--------+
// | | | |
// Auto | 1 | 1 | 3 |
// | | | |
// +--------+--------+--------+
// | | | |
// Star | 4 | 2 | 4 |
// | | | |
// +--------+--------+--------+
//
// The group number indicates the order in which cells are measured.
// Certain order is necessary to be able to dynamically resolve star
// columns / rows sizes which are used as input for measuring of
// the cells belonging to them.
//
// However, there are cases when topology of a grid causes cyclical
// size dependences. For example:
//
//
// column width="Auto" column width="*"
// +----------------------+----------------------+
// | | |
// | | |
// | | |
// | | |
// row height="Auto" | | cell 1 2 |
// | | |
// | | |
// | | |
// | | |
// +----------------------+----------------------+
// | | |
// | | |
// | | |
// | | |
// row height="*" | cell 2 1 | |
// | | |
// | | |
// | | |
// | | |
// +----------------------+----------------------+
//
// In order to accurately calculate constraint width for "cell 1 2"
// (which is the remaining of grid's available width and calculated
// value of Auto column), "cell 2 1" needs to be calculated first,
// as it contributes to the Auto column's calculated value.
// At the same time in order to accurately calculate constraint
// height for "cell 2 1", "cell 1 2" needs to be calcualted first,
// as it contributes to Auto row height, which is used in the
// computation of Star row resolved height.
//
// to "break" this cyclical dependency we are making (arbitrary)
// decision to treat cells like "cell 2 1" as if they appear in Auto
// rows. And then recalculate them one more time when star row
// heights are resolved.
//
// (Or more strictly) the code below implement the following logic:
//
// +---------+
// | enter |
// +---------+
// |
// V
// +----------------+
// | Measure Group1 |
// +----------------+
// |
// V
// / - \
// / \
// Y / Can \ N
// +--------| Resolve |-----------+
// | \ StarsV? / |
// | \ / |
// | \ - / |
// V V
// +----------------+ / - \
// | Resolve StarsV | / \
// +----------------+ Y / Can \ N
// | +----| Resolve |------+
// V | \ StarsU? / |
// +----------------+ | \ / |
// | Measure Group2 | | \ - / |
// +----------------+ | V
// | | +-----------------+
// V | | Measure Group2' |
// +----------------+ | +-----------------+
// | Resolve StarsU | | |
// +----------------+ V V
// | +----------------+ +----------------+
// V | Resolve StarsU | | Resolve StarsU |
// +----------------+ +----------------+ +----------------+
// | Measure Group3 | | |
// +----------------+ V V
// | +----------------+ +----------------+
// | | Measure Group3 | | Measure Group3 |
// | +----------------+ +----------------+
// | | |
// | V V
// | +----------------+ +----------------+
// | | Resolve StarsV | | Resolve StarsV |
// | +----------------+ +----------------+
// | | |
// | | V
// | | +------------------+
// | | | Measure Group2'' |
// | | +------------------+
// | | |
// +----------------------+-------------------------+
// |
// V
// +----------------+
// | Measure Group4 |
// +----------------+
// |
// V
// +--------+
// | exit |
// +--------+
//
// where:
// * all [Measure GroupN] - regular children measure process -
// each cell is measured given contraint size as an input
// and each cell's desired size is accumulated on the
// corresponding column / row;
// * [Measure Group2'] - is when each cell is measured with
// infinit height as a constraint and a cell's desired
// height is ignored;
// * [Measure Groups''] - is when each cell is measured (second
// time during single Grid.MeasureOverride) regularly but its
// returned width is ignored;
//
// This algorithm is believed to be as close to ideal as possible.
// It has the following drawbacks:
// * cells belonging to Group2 can be called to measure twice;
// * iff during second measure a cell belonging to Group2 returns
// desired width greater than desired width returned the first
// time, such a cell is going to be clipped, even though it
// appears in Auto column.
//
MeasureCellsGroup(extData.CellGroup1, constraint, false, false);
{
// after Group1 is measured, only Group3 may have cells belonging to Auto rows.
bool canResolveStarsV = !HasGroup3CellsInAutoRows;
if (canResolveStarsV)
{
if (HasStarCellsV) { ResolveStar(DefinitionsV, constraint.Height); }
MeasureCellsGroup(extData.CellGroup2, constraint, false, false);
if (HasStarCellsU) { ResolveStar(DefinitionsU, constraint.Width); }
MeasureCellsGroup(extData.CellGroup3, constraint, false, false);
}
else
{
// if at least one cell exists in Group2, it must be measured before
// StarsU can be resolved.
bool canResolveStarsU = extData.CellGroup2 > PrivateCells.Length;
if (canResolveStarsU)
{
if (HasStarCellsU) { ResolveStar(DefinitionsU, constraint.Width); }
MeasureCellsGroup(extData.CellGroup3, constraint, false, false);
if (HasStarCellsV) { ResolveStar(DefinitionsV, constraint.Height); }
}
else
{
MeasureCellsGroup(extData.CellGroup2, constraint, false, true);
if (HasStarCellsU) { ResolveStar(DefinitionsU, constraint.Width); }
MeasureCellsGroup(extData.CellGroup3, constraint, false, false);
if (HasStarCellsV) { ResolveStar(DefinitionsV, constraint.Height); }
MeasureCellsGroup(extData.CellGroup2, constraint, true, false);
}
}
}
MeasureCellsGroup(extData.CellGroup4, constraint, false, false);
EnterCounter(Counters._CalculateDesiredSize);
gridDesiredSize = new Size(
CalculateDesiredSize(DefinitionsU),
CalculateDesiredSize(DefinitionsV));
ExitCounter(Counters._CalculateDesiredSize);
}
}
finally
{
MeasureOverrideInProgress = false;
ExitCounterScope(Counters.MeasureOverride);
}
return (gridDesiredSize);
}
///
/// Content arrangement.
///
/// Arrange size
protected override Size ArrangeOverride(Size arrangeSize)
{
try
{
EnterCounterScope(Counters.ArrangeOverride);
ArrangeOverrideInProgress = true;
if (_data == null)
{
UIElementCollection children = InternalChildren;
for (int i = 0, count = children.Count; i < count; ++i)
{
UIElement child = children[i];
if (child != null)
{
child.Arrange(new Rect(arrangeSize));
}
}
}
else
{
Debug.Assert(DefinitionsU.Length > 0 && DefinitionsV.Length > 0);
EnterCounter(Counters._SetFinalSize);
SetFinalSize(DefinitionsU, arrangeSize.Width);
SetFinalSize(DefinitionsV, arrangeSize.Height);
ExitCounter(Counters._SetFinalSize);
UIElementCollection children = InternalChildren;
for (int currentCell = 0; currentCell < PrivateCells.Length; ++currentCell)
{
UIElement cell = children[currentCell];
if (cell == null)
{
continue;
}
int columnIndex = PrivateCells[currentCell].ColumnIndex;
int rowIndex = PrivateCells[currentCell].RowIndex;
int columnSpan = PrivateCells[currentCell].ColumnSpan;
int rowSpan = PrivateCells[currentCell].RowSpan;
Rect cellRect = new Rect(
columnIndex == 0 ? 0.0 : DefinitionsU[columnIndex].FinalOffset,
rowIndex == 0 ? 0.0 : DefinitionsV[rowIndex].FinalOffset,
GetFinalSizeForRange(DefinitionsU, columnIndex, columnSpan),
GetFinalSizeForRange(DefinitionsV, rowIndex, rowSpan) );
EnterCounter(Counters._ArrangeChildHelper2);
cell.Arrange(cellRect);
ExitCounter(Counters._ArrangeChildHelper2);
}
// update render bound on grid lines renderer visual
GridLinesRenderer gridLinesRenderer = EnsureGridLinesRenderer();
if (gridLinesRenderer != null)
{
gridLinesRenderer.UpdateRenderBounds(arrangeSize);
}
}
}
finally
{
SetValid();
ArrangeOverrideInProgress = false;
ExitCounterScope(Counters.ArrangeOverride);
}
return (arrangeSize);
}
///
///
///
protected internal override void OnVisualChildrenChanged(
DependencyObject visualAdded,
DependencyObject visualRemoved)
{
CellsStructureDirty = true;
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
#region Internal Methods
///
/// Invalidates grid caches and makes the grid dirty for measure.
///
internal void Invalidate()
{
CellsStructureDirty = true;
InvalidateMeasure();
}
///
/// Returns final width for a column.
///
///
/// Used from public ColumnDefinition ActualWidth. Calculates final width using offset data.
///
internal double GetFinalColumnDefinitionWidth(int columnIndex)
{
double value = 0.0;
Invariant.Assert(_data != null);
// actual value calculations require structure to be up-to-date
if (!ColumnDefinitionCollectionDirty)
{
DefinitionBase[] definitions = DefinitionsU;
value = definitions[(columnIndex + 1) % definitions.Length].FinalOffset;
if (columnIndex != 0) { value -= definitions[columnIndex].FinalOffset; }
}
return (value);
}
///
/// Returns final height for a row.
///
///
/// Used from public RowDefinition ActualHeight. Calculates final height using offset data.
///
internal double GetFinalRowDefinitionHeight(int rowIndex)
{
double value = 0.0;
Invariant.Assert(_data != null);
// actual value calculations require structure to be up-to-date
if (!RowDefinitionCollectionDirty)
{
DefinitionBase[] definitions = DefinitionsV;
value = definitions[(rowIndex + 1) % definitions.Length].FinalOffset;
if (rowIndex != 0) { value -= definitions[rowIndex].FinalOffset; }
}
return (value);
}
#endregion Internal Methods
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// Convenience accessor to MeasureOverrideInProgress bit flag.
///
internal bool MeasureOverrideInProgress
{
get { return (CheckFlagsAnd(Flags.MeasureOverrideInProgress)); }
set { SetFlags(value, Flags.MeasureOverrideInProgress); }
}
///
/// Convenience accessor to ArrangeOverrideInProgress bit flag.
///
internal bool ArrangeOverrideInProgress
{
get { return (CheckFlagsAnd(Flags.ArrangeOverrideInProgress)); }
set { SetFlags(value, Flags.ArrangeOverrideInProgress); }
}
///
/// Convenience accessor to ValidDefinitionsUStructure bit flag.
///
internal bool ColumnDefinitionCollectionDirty
{
get { return (!CheckFlagsAnd(Flags.ValidDefinitionsUStructure)); }
set { SetFlags(!value, Flags.ValidDefinitionsUStructure); }
}
///
/// Convenience accessor to ValidDefinitionsVStructure bit flag.
///
internal bool RowDefinitionCollectionDirty
{
get { return (!CheckFlagsAnd(Flags.ValidDefinitionsVStructure)); }
set { SetFlags(!value, Flags.ValidDefinitionsVStructure); }
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
///
/// Lays out cells according to rows and columns, and creates lookup grids.
///
private void ValidateCells()
{
EnterCounter(Counters._ValidateCells);
if (CellsStructureDirty)
{
ValidateCellsCore();
CellsStructureDirty = false;
}
ExitCounter(Counters._ValidateCells);
}
///
/// ValidateCellsCore
///
private void ValidateCellsCore()
{
UIElementCollection children = InternalChildren;
ExtendedData extData = ExtData;
extData.CellCachesCollection = new CellCache[children.Count];
extData.CellGroup1 = int.MaxValue;
extData.CellGroup2 = int.MaxValue;
extData.CellGroup3 = int.MaxValue;
extData.CellGroup4 = int.MaxValue;
bool hasStarCellsU = false;
bool hasStarCellsV = false;
bool hasGroup3CellsInAutoRows = false;
for (int i = PrivateCells.Length - 1; i >= 0; --i)
{
UIElement child = children[i];
if (child == null)
{
continue;
}
CellCache cell = new CellCache();
//
// read and cache child positioning properties
//
// read indices from the corresponding properties
// clamp to value < number_of_columns
// column >= 0 is guaranteed by property value validation callback
cell.ColumnIndex = Math.Min(GetColumn(child), DefinitionsU.Length - 1);
// clamp to value < number_of_rows
// row >= 0 is guaranteed by property value validation callback
cell.RowIndex = Math.Min(GetRow(child), DefinitionsV.Length - 1);
// read span properties
// clamp to not exceed beyond right side of the grid
// column_span > 0 is guaranteed by property value validation callback
cell.ColumnSpan = Math.Min(GetColumnSpan(child), DefinitionsU.Length - cell.ColumnIndex);
// clamp to not exceed beyond bottom side of the grid
// row_span > 0 is guaranteed by property value validation callback
cell.RowSpan = Math.Min(GetRowSpan(child), DefinitionsV.Length - cell.RowIndex);
Debug.Assert(0 <= cell.ColumnIndex && cell.ColumnIndex < DefinitionsU.Length);
Debug.Assert(0 <= cell.RowIndex && cell.RowIndex < DefinitionsV.Length);
//
// calculate and cache length types for the child
//
cell.SizeTypeU = GetLengthTypeForRange(DefinitionsU, cell.ColumnIndex, cell.ColumnSpan);
cell.SizeTypeV = GetLengthTypeForRange(DefinitionsV, cell.RowIndex, cell.RowSpan);
hasStarCellsU |= cell.IsStarU;
hasStarCellsV |= cell.IsStarV;
//
// distribute cells into four groups.
//
if (!cell.IsStarV)
{
if (!cell.IsStarU)
{
cell.Next = extData.CellGroup1;
extData.CellGroup1 = i;
}
else
{
cell.Next = extData.CellGroup3;
extData.CellGroup3 = i;
// remember if this cell belongs to auto row
hasGroup3CellsInAutoRows |= cell.IsAutoV;
}
}
else
{
if ( cell.IsAutoU
// note below: if spans through Star column it is NOT Auto
&& !cell.IsStarU )
{
cell.Next = extData.CellGroup2;
extData.CellGroup2 = i;
}
else
{
cell.Next = extData.CellGroup4;
extData.CellGroup4 = i;
}
}
PrivateCells[i] = cell;
}
HasStarCellsU = hasStarCellsU;
HasStarCellsV = hasStarCellsV;
HasGroup3CellsInAutoRows = hasGroup3CellsInAutoRows;
}
///
/// Initializes DefinitionsU memeber either to user supplied ColumnDefinitions collection
/// or to a default single element collection. DefinitionsU gets trimmed to size.
///
///
/// This is one of two methods, where ColumnDefinitions and DefinitionsU are directly accessed.
/// All the rest measure / arrange / render code must use DefinitionsU.
///
private void ValidateDefinitionsUStructure()
{
EnterCounter(Counters._ValidateColsStructure);
if (ColumnDefinitionCollectionDirty)
{
ExtendedData extData = ExtData;
if (extData.ColumnDefinitions == null)
{
if (extData.DefinitionsU == null)
{
extData.DefinitionsU = new DefinitionBase[] { new ColumnDefinition() };
}
}
else
{
extData.ColumnDefinitions.InternalTrimToSize();
if (extData.ColumnDefinitions.InternalCount == 0)
{
// if column definitions collection is empty
// mockup array with one column
extData.DefinitionsU = new DefinitionBase[] { new ColumnDefinition() };
}
else
{
extData.DefinitionsU = extData.ColumnDefinitions.InternalItems;
}
}
ColumnDefinitionCollectionDirty = false;
}
Debug.Assert(ExtData.DefinitionsU != null && ExtData.DefinitionsU.Length > 0);
ExitCounter(Counters._ValidateColsStructure);
}
///
/// Initializes DefinitionsV memeber either to user supplied RowDefinitions collection
/// or to a default single element collection. DefinitionsV gets trimmed to size.
///
///
/// This is one of two methods, where RowDefinitions and DefinitionsV are directly accessed.
/// All the rest measure / arrange / render code must use DefinitionsV.
///
private void ValidateDefinitionsVStructure()
{
EnterCounter(Counters._ValidateRowsStructure);
if (RowDefinitionCollectionDirty)
{
ExtendedData extData = ExtData;
if (extData.RowDefinitions == null)
{
if (extData.DefinitionsV == null)
{
extData.DefinitionsV = new DefinitionBase[] { new RowDefinition() };
}
}
else
{
extData.RowDefinitions.InternalTrimToSize();
if (extData.RowDefinitions.InternalCount == 0)
{
// if row definitions collection is empty
// mockup array with one row
extData.DefinitionsV = new DefinitionBase[] { new RowDefinition() };
}
else
{
extData.DefinitionsV = extData.RowDefinitions.InternalItems;
}
}
RowDefinitionCollectionDirty = false;
}
Debug.Assert(ExtData.DefinitionsV != null && ExtData.DefinitionsV.Length > 0);
ExitCounter(Counters._ValidateRowsStructure);
}
///
/// Validates layout time size type information on given array of definitions.
/// Sets MinSize and MeasureSizes.
///
/// Array of definitions to update.
/// if "true" then star definitions are treated as Auto.
private void ValidateDefinitionsLayout(
DefinitionBase[] definitions,
bool treatStarAsAuto)
{
for (int i = 0; i < definitions.Length; ++i)
{
definitions[i].OnBeforeLayout(this);
double userMinSize = definitions[i].UserMinSize;
double userMaxSize = definitions[i].UserMaxSize;
double userSize = 0;
switch (definitions[i].UserSize.GridUnitType)
{
case (GridUnitType.Pixel):
definitions[i].SizeType = LayoutTimeSizeType.Pixel;
userSize = definitions[i].UserSize.Value;
// this was brought with NewLayout and defeats squishy behavior
userMinSize = Math.Max(userMinSize, Math.Min(userSize, userMaxSize));
break;
case (GridUnitType.Auto):
definitions[i].SizeType = LayoutTimeSizeType.Auto;
userSize = double.PositiveInfinity;
break;
case (GridUnitType.Star):
if (treatStarAsAuto)
{
definitions[i].SizeType = LayoutTimeSizeType.Auto;
userSize = double.PositiveInfinity;
}
else
{
definitions[i].SizeType = LayoutTimeSizeType.Star;
userSize = double.PositiveInfinity;
}
break;
default:
Debug.Assert(false);
break;
}
definitions[i].UpdateMinSize(userMinSize);
definitions[i].MeasureSize = Math.Max(userMinSize, Math.Min(userSize, userMaxSize));
}
}
///
/// Measures one group of cells.
///
/// Head index of the cells chain.
/// Reference size for spanned cells
/// calculations.
/// When "true" cells' desired
/// width is not registered in columns.
/// Passed through to MeasureCell.
/// When "true" cells' desired height is not registered in rows.
private void MeasureCellsGroup(
int cellsHead,
Size referenceSize,
bool ignoreDesiredSizeU,
bool forceInfinityV)
{
if (cellsHead >= PrivateCells.Length)
{
return;
}
UIElementCollection children = InternalChildren;
Hashtable spanStore = null;
bool ignoreDesiredSizeV = forceInfinityV;
int i = cellsHead;
do
{
MeasureCell(i, forceInfinityV);
if (!ignoreDesiredSizeU)
{
if (PrivateCells[i].ColumnSpan == 1)
{
DefinitionsU[PrivateCells[i].ColumnIndex].UpdateMinSize(children[i].DesiredSize.Width);
}
else
{
RegisterSpan(
ref spanStore,
PrivateCells[i].ColumnIndex,
PrivateCells[i].ColumnSpan,
true,
children[i].DesiredSize.Width);
}
}
if (!ignoreDesiredSizeV)
{
if (PrivateCells[i].RowSpan == 1)
{
DefinitionsV[PrivateCells[i].RowIndex].UpdateMinSize(children[i].DesiredSize.Height);
}
else
{
RegisterSpan(
ref spanStore,
PrivateCells[i].RowIndex,
PrivateCells[i].RowSpan,
false,
children[i].DesiredSize.Height);
}
}
i = PrivateCells[i].Next;
} while (i < PrivateCells.Length);
if (spanStore != null)
{
foreach (DictionaryEntry e in spanStore)
{
SpanKey key = (SpanKey)e.Key;
double requestedSize = (double)e.Value;
EnsureMinSizeInDefinitionRange(
key.U ? DefinitionsU : DefinitionsV,
key.Start,
key.Count,
requestedSize,
key.U ? referenceSize.Width : referenceSize.Height);
}
}
}
///
/// Helper method to register a span information for delayed processing.
///
/// Reference to a hashtable object used as storage.
/// Span starting index.
/// Span count.
/// true if this is a column span. false if this is a row span.
/// Value to store. If an entry already exists the biggest value is stored.
private static void RegisterSpan(
ref Hashtable store,
int start,
int count,
bool u,
double value)
{
if (store == null)
{
store = new Hashtable();
}
SpanKey key = new SpanKey(start, count, u);
object o = store[key];
if ( o == null
|| value > (double)o )
{
store[key] = value;
}
}
///
/// Takes care of measuring a single cell.
///
/// Index of the cell to measure.
/// If "true" then cell is always
/// calculated to infinite height.
private void MeasureCell(
int cell,
bool forceInfinityV)
{
EnterCounter(Counters._MeasureCell);
double cellMeasureWidth;
double cellMeasureHeight;
if ( PrivateCells[cell].IsAutoU
&& !PrivateCells[cell].IsStarU )
{
// if cell belongs to at least one Auto column and not a single Star column
// then it should be calculated "to content", thus it is possible to "shortcut"
// calculations and simply assign PositiveInfinity here.
cellMeasureWidth = double.PositiveInfinity;
}
else
{
// otherwise...
cellMeasureWidth = GetMeasureSizeForRange(
DefinitionsU,
PrivateCells[cell].ColumnIndex,
PrivateCells[cell].ColumnSpan);
}
if (forceInfinityV)
{
cellMeasureHeight = double.PositiveInfinity;
}
else if ( PrivateCells[cell].IsAutoV
&& !PrivateCells[cell].IsStarV )
{
// if cell belongs to at least one Auto row and not a single Star row
// then it should be calculated "to content", thus it is possible to "shortcut"
// calculations and simply assign PositiveInfinity here.
cellMeasureHeight = double.PositiveInfinity;
}
else
{
cellMeasureHeight = GetMeasureSizeForRange(
DefinitionsV,
PrivateCells[cell].RowIndex,
PrivateCells[cell].RowSpan);
}
EnterCounter(Counters.__MeasureChild);
UIElement child = InternalChildren[cell];
if (child != null)
{
child.Measure(new Size(cellMeasureWidth, cellMeasureHeight));
}
ExitCounter(Counters.__MeasureChild);
ExitCounter(Counters._MeasureCell);
}
///
/// Calculates one dimensional measure size for given definitions' range.
///
/// Source array of definitions to read values from.
/// Starting index of the range.
/// Number of definitions included in the range.
/// Calculated measure size.
///
/// For "Auto" definitions MinWidth is used in place of PreferredSize.
///
private double GetMeasureSizeForRange(
DefinitionBase[] definitions,
int start,
int count)
{
Debug.Assert(0 < count && 0 <= start && (start + count) <= definitions.Length);
double measureSize = 0;
int i = start + count - 1;
do
{
measureSize += (definitions[i].SizeType == LayoutTimeSizeType.Auto)
? definitions[i].MinSize
: definitions[i].MeasureSize;
} while (--i >= start);
return (measureSize);
}
///
/// Accumulates length type information for given definition's range.
///
/// Source array of definitions to read values from.
/// Starting index of the range.
/// Number of definitions included in the range.
/// Length type for given range.
private LayoutTimeSizeType GetLengthTypeForRange(
DefinitionBase[] definitions,
int start,
int count)
{
Debug.Assert(0 < count && 0 <= start && (start + count) <= definitions.Length);
LayoutTimeSizeType lengthType = LayoutTimeSizeType.None;
int i = start + count - 1;
do
{
lengthType |= definitions[i].SizeType;
} while (--i >= start);
return (lengthType);
}
///
/// Distributes min size back to definition array's range.
///
/// Start of the range.
/// Number of items in the range.
/// Minimum size that should "fit" into the definitions range.
/// Definition array receiving distribution.
/// Size used to resolve percentages.
private void EnsureMinSizeInDefinitionRange(
DefinitionBase[] definitions,
int start,
int count,
double requestedSize,
double percentReferenceSize)
{
Debug.Assert(1 < count && 0 <= start && (start + count) <= definitions.Length);
// avoid processing when asked to distribute "0"
if (!_IsZero(requestedSize))
{
DefinitionBase[] tempDefinitions = TempDefinitions; // temp array used to remember definitions for sorting
int end = start + count;
int autoDefinitionsCount = 0;
double rangeMinSize = 0;
double rangePreferredSize = 0;
double rangeMaxSize = 0;
double maxMaxSize = 0; // maximum of maximum sizes
// first accumulate the necessary information:
// a) sum up the sizes in the range;
// b) count the number of auto definitions in the range;
// c) initialize temp array
// d) cache the maximum size into SizeCache
// e) accumulate max of max sizes
for (int i = start; i < end; ++i)
{
double minSize = definitions[i].MinSize;
double preferredSize = definitions[i].PreferredSize;
double maxSize = Math.Max(definitions[i].UserMaxSize, minSize);
rangeMinSize += minSize;
rangePreferredSize += preferredSize;
rangeMaxSize += maxSize;
definitions[i].SizeCache = maxSize;
// sanity check: no matter what, but min size must always be the smaller;
// max size must be the biggest; and preferred should be in between
Debug.Assert( minSize <= preferredSize
&& preferredSize <= maxSize
&& rangeMinSize <= rangePreferredSize
&& rangePreferredSize <= rangeMaxSize );
if (maxMaxSize < maxSize) maxMaxSize = maxSize;
if (definitions[i].UserSize.IsAuto) autoDefinitionsCount++;
tempDefinitions[i - start] = definitions[i];
}
// avoid processing if the range already big enough
if (requestedSize > rangeMinSize)
{
if (requestedSize <= rangePreferredSize)
{
//
// requestedSize fits into preferred size of the range.
// distribute according to the following logic:
// * do not distribute into auto definitions - they should continue to stay "tight";
// * for all non-auto definitions distribute to equi-size min sizes, without exceeding preferred size.
//
// in order to achieve that, definitions are sorted in a way that all auto definitions
// are first, then definitions follow ascending order with PreferredSize as the key of sorting.
//
double sizeToDistribute;
int i;
Array.Sort(tempDefinitions, 0, count, s_spanPreferredDistributionOrderComparer);
for (i = 0, sizeToDistribute = requestedSize; i < autoDefinitionsCount; ++i)
{
// sanity check: only auto definitions allowed in this loop
Debug.Assert(tempDefinitions[i].UserSize.IsAuto);
// adjust sizeToDistribute value by subtracting auto definition min size
sizeToDistribute -= (tempDefinitions[i].MinSize);
}
for (; i < count; ++i)
{
// sanity check: no auto definitions allowed in this loop
Debug.Assert(!tempDefinitions[i].UserSize.IsAuto);
double newMinSize = Math.Min(sizeToDistribute / (count - i), tempDefinitions[i].PreferredSize);
if (newMinSize > tempDefinitions[i].MinSize) { tempDefinitions[i].UpdateMinSize(newMinSize); }
sizeToDistribute -= newMinSize;
}
// sanity check: requested size must all be distributed
Debug.Assert(_IsZero(sizeToDistribute));
}
else if (requestedSize <= rangeMaxSize)
{
//
// requestedSize bigger than preferred size, but fit into max size of the range.
// distribute according to the following logic:
// * do not distribute into auto definitions, if possible - they should continue to stay "tight";
// * for all non-auto definitions distribute to euqi-size min sizes, without exceeding max size.
//
// in order to achieve that, definitions are sorted in a way that all non-auto definitions
// are last, then definitions follow ascending order with MaxSize as the key of sorting.
//
double sizeToDistribute;
int i;
Array.Sort(tempDefinitions, 0, count, s_spanMaxDistributionOrderComparer);
for (i = 0, sizeToDistribute = requestedSize - rangePreferredSize; i < count - autoDefinitionsCount; ++i)
{
// sanity check: no auto definitions allowed in this loop
Debug.Assert(!tempDefinitions[i].UserSize.IsAuto);
double preferredSize = tempDefinitions[i].PreferredSize;
double newMinSize = preferredSize + sizeToDistribute / (count - autoDefinitionsCount - i);
tempDefinitions[i].UpdateMinSize(Math.Min(newMinSize, tempDefinitions[i].SizeCache));
sizeToDistribute -= (tempDefinitions[i].MinSize - preferredSize);
}
for (; i < count; ++i)
{
// sanity check: only auto definitions allowed in this loop
Debug.Assert(tempDefinitions[i].UserSize.IsAuto);
double preferredSize = tempDefinitions[i].MinSize;
double newMinSize = preferredSize + sizeToDistribute / (count - i);
tempDefinitions[i].UpdateMinSize(Math.Min(newMinSize, tempDefinitions[i].SizeCache));
sizeToDistribute -= (tempDefinitions[i].MinSize - preferredSize);
}
// sanity check: requested size must all be distributed
Debug.Assert(_IsZero(sizeToDistribute));
}
else
{
//
// requestedSize bigger than max size of the range.
// distribute according to the following logic:
// * for all definitions distribute to equi-size min sizes.
//
double equalSize = requestedSize / count;
if ( equalSize < maxMaxSize
&& !_AreClose(equalSize, maxMaxSize) )
{
// equi-size is less than maximum of maxSizes.
// in this case distribute so that smaller definitions grow faster than
// bigger ones.
double totalRemainingSize = maxMaxSize * count - rangeMaxSize;
double sizeToDistribute = requestedSize - rangeMaxSize;
// sanity check: totalRemainingSize and sizeToDistribute must be real positive numbers
Debug.Assert( !double.IsInfinity(totalRemainingSize)
&& !DoubleUtil.IsNaN(totalRemainingSize)
&& totalRemainingSize > 0
&& !double.IsInfinity(sizeToDistribute)
&& !DoubleUtil.IsNaN(sizeToDistribute)
&& sizeToDistribute > 0 );
for (int i = 0; i < count; ++i)
{
double deltaSize = (maxMaxSize - tempDefinitions[i].SizeCache) * sizeToDistribute / totalRemainingSize;
tempDefinitions[i].UpdateMinSize(tempDefinitions[i].SizeCache + deltaSize);
}
}
else
{
//
// equi-size is greater or equal to maximum of max sizes.
// all definitions receive equalSize as their mim sizes.
//
for (int i = 0; i < count; ++i)
{
tempDefinitions[i].UpdateMinSize(equalSize);
}
}
}
}
}
}
///
/// Resolves Star's for given array of definitions.
///
/// Array of definitions to resolve stars.
/// All available size.
///
/// Must initialize LayoutSize for all Star entries in given array of definitions.
///
private void ResolveStar(
DefinitionBase[] definitions,
double availableSize)
{
DefinitionBase[] tempDefinitions = TempDefinitions;
int starDefinitionsCount = 0;
double takenSize = 0;
for (int i = 0; i < definitions.Length; ++i)
{
switch (definitions[i].SizeType)
{
case (LayoutTimeSizeType.Auto):
takenSize += definitions[i].MinSize;
break;
case (LayoutTimeSizeType.Pixel):
takenSize += definitions[i].MeasureSize;
break;
case (LayoutTimeSizeType.Star):
{
tempDefinitions[starDefinitionsCount++] = definitions[i];
double starValue = definitions[i].UserSize.Value;
if (_IsZero(starValue))
{
definitions[i].MeasureSize = 0;
definitions[i].SizeCache = 0;
}
else
{
// clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values
// can be summed up without overflow
starValue = Math.Min(starValue, c_starClip);
// Note: normalized star value is temporary cached into MeasureSize
definitions[i].MeasureSize = starValue;
double maxSize = Math.Max(definitions[i].MinSize, definitions[i].UserMaxSize);
maxSize = Math.Min(maxSize, c_starClip);
definitions[i].SizeCache = maxSize / starValue;
}
}
break;
}
}
if (starDefinitionsCount > 0)
{
Array.Sort(tempDefinitions, 0, starDefinitionsCount, s_starDistributionOrderComparer);
// the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow...
// partial sum value is stored in each definition's SizeCache member.
// this way the algorithm guarantees (starValue <= definition.SizeCache) and thus
// (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero.
// this is an important change from previous implementation where the following was possible:
// ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0...
double allStarWeights = 0;
int i = starDefinitionsCount - 1;
do
{
allStarWeights += tempDefinitions[i].MeasureSize;
tempDefinitions[i].SizeCache = allStarWeights;
} while (--i >= 0);
i = 0;
do
{
double resolvedSize;
double starValue = tempDefinitions[i].MeasureSize;
if (_IsZero(starValue))
{
resolvedSize = tempDefinitions[i].MinSize;
}
else
{
double userSize = Math.Max(availableSize - takenSize, 0.0) * (starValue / tempDefinitions[i].SizeCache);
resolvedSize = Math.Min(userSize, tempDefinitions[i].UserMaxSize);
resolvedSize = Math.Max(tempDefinitions[i].MinSize, resolvedSize);
}
tempDefinitions[i].MeasureSize = resolvedSize;
takenSize += resolvedSize;
} while (++i < starDefinitionsCount);
}
}
///
/// Calculates desired size for given array of definitions.
///
/// Array of definitions to use for calculations.
/// Desired size.
private double CalculateDesiredSize(
DefinitionBase[] definitions)
{
double desiredSize = 0;
for (int i = 0; i < definitions.Length; ++i)
{
desiredSize += definitions[i].MinSize;
}
return (desiredSize);
}
///
/// Calculates and sets final size for all definitions in the given array.
///
/// Array of definitions to process.
/// Final size to lay out to.
private void SetFinalSize(
DefinitionBase[] definitions,
double finalSize)
{
DefinitionBase[] tempDefinitions = TempDefinitions; // temp array used to remember definitions indices
int starDefinitionsCount = 0; // traverses form the first entry up
int nonStarIndex = definitions.Length; // traverses from the last entry down
double allPreferredArrangeSize = 0;
for (int i = 0; i < definitions.Length; ++i)
{
// if definition is shared then is cannot be star
Debug.Assert(!definitions[i].IsShared || !definitions[i].UserSize.IsStar);
if (definitions[i].UserSize.IsStar)
{
tempDefinitions[starDefinitionsCount++] = definitions[i];
double starValue = definitions[i].UserSize.Value;
if (_IsZero(starValue))
{
// cach normilized star value temporary into MeasureSize
definitions[i].MeasureSize = 0;
definitions[i].SizeCache = 0;
}
else
{
// clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values
// can be summed up without overflow
starValue = Math.Min(starValue, c_starClip);
// Note: normalized star value is temporary cached into MeasureSize
definitions[i].MeasureSize = starValue;
double maxSize = Math.Max(definitions[i].MinSizeForArrange, definitions[i].UserMaxSize);
maxSize = Math.Min(maxSize, c_starClip);
definitions[i].SizeCache = maxSize / starValue;
}
}
else
{
tempDefinitions[--nonStarIndex] = definitions[i];
double userSize = 0;
switch (definitions[i].UserSize.GridUnitType)
{
case (GridUnitType.Pixel):
userSize = definitions[i].UserSize.Value;
break;
case (GridUnitType.Auto):
userSize = definitions[i].MinSizeForArrange;
break;
}
double userMaxSize;
if (definitions[i].IsShared)
{
// overriding userMaxSize effectively prevents squishy-ness.
// this is a "solution" to avoid shared definitions from been sized to
// different final size at arrange time, if / when different grids receive
// different final sizes.
userMaxSize = userSize;
}
else
{
userMaxSize = definitions[i].UserMaxSize;
}
definitions[i].SizeCache = Math.Max(definitions[i].MinSizeForArrange, Math.Min(userSize, userMaxSize));
allPreferredArrangeSize += definitions[i].SizeCache;
}
}
// indices should meet
Debug.Assert(nonStarIndex == starDefinitionsCount);
if (starDefinitionsCount > 0)
{
Array.Sort(tempDefinitions, 0, starDefinitionsCount, s_starDistributionOrderComparer);
// the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow...
// partial sum value is stored in each definition's SizeCache member.
// this way the algorithm guarantees (starValue <= definition.SizeCache) and thus
// (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero.
// this is an important change from previous implementation where the following was possible:
// ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0...
double allStarWeights = 0;
int i = starDefinitionsCount - 1;
do
{
allStarWeights += tempDefinitions[i].MeasureSize;
tempDefinitions[i].SizeCache = allStarWeights;
} while (--i >= 0);
i = 0;
do
{
double resolvedSize;
double starValue = tempDefinitions[i].MeasureSize;
if (_IsZero(starValue))
{
resolvedSize = tempDefinitions[i].MinSizeForArrange;
}
else
{
double userSize = Math.Max(finalSize - allPreferredArrangeSize, 0.0) * (starValue / tempDefinitions[i].SizeCache);
resolvedSize = Math.Min(userSize, tempDefinitions[i].UserMaxSize);
resolvedSize = Math.Max(tempDefinitions[i].MinSizeForArrange, resolvedSize);
}
tempDefinitions[i].SizeCache = resolvedSize;
allPreferredArrangeSize += resolvedSize;
} while (++i < starDefinitionsCount);
}
if ( allPreferredArrangeSize > finalSize
&& !_AreClose(allPreferredArrangeSize, finalSize) )
{
Array.Sort(tempDefinitions, 0, definitions.Length, s_distributionOrderComparer);
double sizeToDistribute = finalSize - allPreferredArrangeSize;
for (int i = 0; i < definitions.Length; ++i)
{
double final = tempDefinitions[i].SizeCache + (sizeToDistribute / (definitions.Length - i));
final = Math.Max(final, tempDefinitions[i].MinSizeForArrange);
final = Math.Min(final, tempDefinitions[i].SizeCache);
sizeToDistribute -= (final - tempDefinitions[i].SizeCache);
tempDefinitions[i].SizeCache = final;
}
}
definitions[0].FinalOffset = 0.0;
for (int i = 0; i < definitions.Length; ++i)
{
definitions[(i + 1) % definitions.Length].FinalOffset = definitions[i].FinalOffset + definitions[i].SizeCache;
}
}
///
/// Calculates final (aka arrange) size for given range.
///
/// Array of definitions to process.
/// Start of the range.
/// Number of items in the range.
/// Final size.
private double GetFinalSizeForRange(
DefinitionBase[] definitions,
int start,
int count)
{
double size = 0;
int i = start + count - 1;
do
{
size += definitions[i].SizeCache;
} while (--i >= start);
return (size);
}
///
/// Clears dirty state for the grid and its columns / rows
///
private void SetValid()
{
ExtendedData extData = ExtData;
if (extData != null)
{
// for (int i = 0; i < PrivateColumnCount; ++i) DefinitionsU[i].SetValid ();
// for (int i = 0; i < PrivateRowCount; ++i) DefinitionsV[i].SetValid ();
if (extData.TempDefinitions != null)
{
// TempDefinitions has to be cleared to avoid "memory leaks"
Array.Clear(extData.TempDefinitions, 0, Math.Max(DefinitionsU.Length, DefinitionsV.Length));
extData.TempDefinitions = null;
}
}
}
///
/// Returns true if ColumnDefinitions collection is not empty
///
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeColumnDefinitions()
{
ExtendedData extData = ExtData;
return ( extData != null
&& extData.ColumnDefinitions != null
&& extData.ColumnDefinitions.Count > 0 );
}
///
/// Returns true if RowDefinitions collection is not empty
///
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeRowDefinitions()
{
ExtendedData extData = ExtData;
return ( extData != null
&& extData.RowDefinitions != null
&& extData.RowDefinitions.Count > 0 );
}
///
/// Synchronized ShowGridLines property with the state of the grid's visual collection
/// by adding / removing GridLinesRenderer visual.
/// Returns a reference to GridLinesRenderer visual or null.
///
private GridLinesRenderer EnsureGridLinesRenderer()
{
//
// synchronize the state
//
if (ShowGridLines && (_gridLinesRenderer == null))
{
_gridLinesRenderer = new GridLinesRenderer();
this.AddVisualChild(_gridLinesRenderer);
}
if ((!ShowGridLines) && (_gridLinesRenderer != null))
{
this.RemoveVisualChild(_gridLinesRenderer);
_gridLinesRenderer = null;
}
return (_gridLinesRenderer);
}
///
/// SetFlags is used to set or unset one or multiple
/// flags on the object.
///
private void SetFlags(bool value, Flags flags)
{
_flags = value ? (_flags | flags) : (_flags & (~flags));
}
///
/// CheckFlagsAnd returns true if all the flags in the
/// given bitmask are set on the object.
///
private bool CheckFlagsAnd(Flags flags)
{
return ((_flags & flags) == flags);
}
///
/// CheckFlagsOr returns true if at least one flag in the
/// given bitmask is set.
///
///
/// If no bits are set in the given bitmask, the method returns
/// true .
///
private bool CheckFlagsOr(Flags flags)
{
return (flags == 0 || (_flags & flags) != 0);
}
///
///
///
private static void OnShowGridLinesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Grid grid = (Grid)d;
if ( grid.ExtData != null // trivial grid is 1 by 1. there is no grid lines anyway
&& grid.ListenToNotifications)
{
grid.InvalidateVisual();
}
grid.SetFlags((bool) e.NewValue, Flags.ShowGridLinesPropertyValue);
}
///
///
///
private static void OnCellAttachedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Visual child = d as Visual;
if (child != null)
{
Grid grid = VisualTreeHelper.GetParent(child) as Grid;
if ( grid != null
&& grid.ExtData != null
&& grid.ListenToNotifications )
{
grid.CellsStructureDirty = true;
grid.InvalidateMeasure();
}
}
}
///
///
///
private static bool IsIntValueNotNegative(object value)
{
return ((int)value >= 0);
}
///
///
///
private static bool IsIntValueGreaterThanZero(object value)
{
return ((int)value > 0);
}
#endregion Private Methods
//------------------------------------------------------
//
// Private Properties
//
//-----------------------------------------------------
#region Private Properties
///
/// Private version returning array of column definitions.
///
private DefinitionBase[] DefinitionsU
{
get { return (ExtData.DefinitionsU); }
}
///
/// Private version returning array of row definitions.
///
private DefinitionBase[] DefinitionsV
{
get { return (ExtData.DefinitionsV); }
}
///
/// Helper accessor to layout time array of definitions.
///
private DefinitionBase[] TempDefinitions
{
get
{
ExtendedData extData = ExtData;
if (extData.TempDefinitions == null)
{
int requiredLength = Math.Max(DefinitionsU.Length, DefinitionsV.Length);
WeakReference tempDefinitionsWeakRef = (WeakReference)Thread.GetData(s_tempDefinitionsDataSlot);
if (tempDefinitionsWeakRef == null)
{
extData.TempDefinitions = new DefinitionBase[requiredLength * 2];
Thread.SetData(s_tempDefinitionsDataSlot, new WeakReference(extData.TempDefinitions));
}
else
{
extData.TempDefinitions = (DefinitionBase[])tempDefinitionsWeakRef.Target;
if ( extData.TempDefinitions == null
|| extData.TempDefinitions.Length < requiredLength )
{
extData.TempDefinitions = new DefinitionBase[requiredLength * 2];
tempDefinitionsWeakRef.Target = extData.TempDefinitions;
}
}
}
return (extData.TempDefinitions);
}
}
///
/// Private version returning array of cells.
///
private CellCache[] PrivateCells
{
get { return (ExtData.CellCachesCollection); }
}
///
/// Convenience accessor to ValidCellsStructure bit flag.
///
private bool CellsStructureDirty
{
get { return (!CheckFlagsAnd(Flags.ValidCellsStructure)); }
set { SetFlags(!value, Flags.ValidCellsStructure); }
}
///
/// Convenience accessor to ListenToNotifications bit flag.
///
private bool ListenToNotifications
{
get { return (CheckFlagsAnd(Flags.ListenToNotifications)); }
set { SetFlags(value, Flags.ListenToNotifications); }
}
///
/// Convenience accessor to SizeToContentU bit flag.
///
private bool SizeToContentU
{
get { return (CheckFlagsAnd(Flags.SizeToContentU)); }
set { SetFlags(value, Flags.SizeToContentU); }
}
///
/// Convenience accessor to SizeToContentV bit flag.
///
private bool SizeToContentV
{
get { return (CheckFlagsAnd(Flags.SizeToContentV)); }
set { SetFlags(value, Flags.SizeToContentV); }
}
///
/// Convenience accessor to HasStarCellsU bit flag.
///
private bool HasStarCellsU
{
get { return (CheckFlagsAnd(Flags.HasStarCellsU)); }
set { SetFlags(value, Flags.HasStarCellsU); }
}
///
/// Convenience accessor to HasStarCellsV bit flag.
///
private bool HasStarCellsV
{
get { return (CheckFlagsAnd(Flags.HasStarCellsV)); }
set { SetFlags(value, Flags.HasStarCellsV); }
}
///
/// Convenience accessor to HasGroup3CellsInAutoRows bit flag.
///
private bool HasGroup3CellsInAutoRows
{
get { return (CheckFlagsAnd(Flags.HasGroup3CellsInAutoRows)); }
set { SetFlags(value, Flags.HasGroup3CellsInAutoRows); }
}
///
/// fp version of d == 0 .
///
/// Value to check.
/// true if d == 0.
private static bool _IsZero(double d)
{
return (Math.Abs(d) < c_epsilon);
}
///
/// fp version of d1 == d2
///
/// First value to compare
/// Second value to compare
/// true if d1 == d2
private static bool _AreClose(double d1, double d2)
{
return (Math.Abs(d1 - d2) < c_epsilon);
}
///
/// Returns reference to extended data bag.
///
private ExtendedData ExtData
{
get { return (_data); }
}
#endregion Private Properties
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
private ExtendedData _data; // extended data instantiated on demand, for non-trivial case handling only
private Flags _flags; // grid validity / property caches dirtiness flags
private GridLinesRenderer _gridLinesRenderer;
#endregion Private Fields
//-----------------------------------------------------
//
// Static Fields
//
//-----------------------------------------------------
#region Static Fields
private const double c_epsilon = 1e-5; // used in fp calculations
private const double c_starClip = 1e298; // used as maximum for clipping star values during normalization
private static readonly LocalDataStoreSlot s_tempDefinitionsDataSlot = Thread.AllocateDataSlot();
private static readonly IComparer s_spanPreferredDistributionOrderComparer = new SpanPreferredDistributionOrderComparer();
private static readonly IComparer s_spanMaxDistributionOrderComparer = new SpanMaxDistributionOrderComparer();
private static readonly IComparer s_starDistributionOrderComparer = new StarDistributionOrderComparer();
private static readonly IComparer s_distributionOrderComparer = new DistributionOrderComparer();
#endregion Static Fields
//------------------------------------------------------
//
// Private Structures / Classes
//
//-----------------------------------------------------
#region Private Structures Classes
///
/// Extended data instantiated on demand, when grid handles non-trivial case.
///
private class ExtendedData
{
internal ColumnDefinitionCollection ColumnDefinitions; // collection of column definitions (logical tree support)
internal RowDefinitionCollection RowDefinitions; // collection of row definitions (logical tree support)
internal DefinitionBase[] DefinitionsU; // collection of column definitions used during calc
internal DefinitionBase[] DefinitionsV; // collection of row definitions used during calc
internal CellCache[] CellCachesCollection; // backing store for logical children
internal int CellGroup1; // index of the first cell in first cell group
internal int CellGroup2; // index of the first cell in second cell group
internal int CellGroup3; // index of the first cell in third cell group
internal int CellGroup4; // index of the first cell in forth cell group
internal DefinitionBase[] TempDefinitions; // temporary array used during layout for various purposes
// TempDefinitions.Length == Max(definitionsU.Length, definitionsV.Length)
}
///
/// Grid validity / property caches dirtiness flags
///
[System.Flags]
private enum Flags
{
//
// the foolowing flags let grid tracking dirtiness in more granular manner:
// * Valid???Structure flags indicate that elements were added or removed.
// * Valid???Layout flags indicate that layout time portion of the information
// stored on the objects should be updated.
//
ValidDefinitionsUStructure = 0x00000001,
ValidDefinitionsVStructure = 0x00000002,
ValidCellsStructure = 0x00000004,
//
// boolean properties state
//
ShowGridLinesPropertyValue = 0x00000100, // show grid lines ?
//
// boolean flags
//
ListenToNotifications = 0x00001000, // "0" when all notifications are ignored
SizeToContentU = 0x00002000, // "1" if calculating to content in U direction
SizeToContentV = 0x00004000, // "1" if calculating to content in V direction
HasStarCellsU = 0x00008000, // "1" if at least one cell belongs to a Star column
HasStarCellsV = 0x00010000, // "1" if at least one cell belongs to a Star row
HasGroup3CellsInAutoRows = 0x00020000, // "1" if at least one cell of group 3 belongs to an Auto row
MeasureOverrideInProgress = 0x00040000, // "1" while in the context of Grid.MeasureOverride
ArrangeOverrideInProgress = 0x00080000, // "1" while in the context of Grid.ArrangeOverride
}
#endregion Private Structures Classes
//------------------------------------------------------
//
// Properties
//
//------------------------------------------------------
#region Properties
///
/// ShowGridLines property. This property is used mostly
/// for simplification of visual debuggig. When it is set
/// to true grid lines are drawn to visualize location
/// of grid lines.
///
public static readonly DependencyProperty ShowGridLinesProperty =
DependencyProperty.Register(
"ShowGridLines",
typeof(bool),
typeof(Grid),
new FrameworkPropertyMetadata(
false,
new PropertyChangedCallback(OnShowGridLinesPropertyChanged)));
///
/// Column property. This is an attached property.
/// Grid defines Column property, so that it can be set
/// on any element treated as a cell. Column property
/// specifies child's position with respect to columns.
///
///
/// Columns are 0 - based. In order to appear in first column, element
/// should have Column property set to 0 .
/// Default value for the property is 0 .
///
[CommonDependencyProperty]
public static readonly DependencyProperty ColumnProperty =
DependencyProperty.RegisterAttached(
"Column",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
0,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueNotNegative));
///
/// Row property. This is an attached property.
/// Grid defines Row, so that it can be set
/// on any element treated as a cell. Row property
/// specifies child's position with respect to rows.
///
/// Rows are 0 - based. In order to appear in first row, element
/// should have Row property set to 0 .
/// Default value for the property is 0 .
///
///
[CommonDependencyProperty]
public static readonly DependencyProperty RowProperty =
DependencyProperty.RegisterAttached(
"Row",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
0,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueNotNegative));
///
/// ColumnSpan property. This is an attached property.
/// Grid defines ColumnSpan, so that it can be set
/// on any element treated as a cell. ColumnSpan property
/// specifies child's width with respect to columns.
/// Example, ColumnSpan == 2 means that child will span across two columns.
///
///
/// Default value for the property is 1 .
///
[CommonDependencyProperty]
public static readonly DependencyProperty ColumnSpanProperty =
DependencyProperty.RegisterAttached(
"ColumnSpan",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
1,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueGreaterThanZero));
///
/// RowSpan property. This is an attached property.
/// Grid defines RowSpan, so that it can be set
/// on any element treated as a cell. RowSpan property
/// specifies child's height with respect to row grid lines.
/// Example, RowSpan == 3 means that child will span across three rows.
///
///
/// Default value for the property is 1 .
///
[CommonDependencyProperty]
public static readonly DependencyProperty RowSpanProperty =
DependencyProperty.RegisterAttached(
"RowSpan",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
1,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueGreaterThanZero));
///
/// IsSharedSizeScope property marks scoping element for shared size.
///
public static readonly DependencyProperty IsSharedSizeScopeProperty =
DependencyProperty.RegisterAttached(
"IsSharedSizeScope",
typeof(bool),
typeof(Grid),
new FrameworkPropertyMetadata(
false,
new PropertyChangedCallback(DefinitionBase.OnIsSharedSizeScopePropertyChanged)));
#endregion Properties
//-----------------------------------------------------
//
// Internal Structures / Classes
//
//------------------------------------------------------
#region Internal Structures Classes
///
/// LayoutTimeSizeType is used internally and reflects layout-time size type.
///
[System.Flags]
internal enum LayoutTimeSizeType : byte
{
None = 0x00,
Pixel = 0x01,
Auto = 0x02,
Star = 0x04,
}
#endregion Internal Structures Classes
//-----------------------------------------------------
//
// Private Structures / Classes
//
//-----------------------------------------------------
#region Private Structures Classes
///
/// CellCache stored calculated values of
/// 1. attached cell positioning properties;
/// 2. size type;
/// 3. index of a next cell in the group;
///
private struct CellCache
{
internal int ColumnIndex;
internal int RowIndex;
internal int ColumnSpan;
internal int RowSpan;
internal LayoutTimeSizeType SizeTypeU;
internal LayoutTimeSizeType SizeTypeV;
internal int Next;
internal bool IsStarU { get { return ((SizeTypeU & LayoutTimeSizeType.Star) != 0); } }
internal bool IsAutoU { get { return ((SizeTypeU & LayoutTimeSizeType.Auto) != 0); } }
internal bool IsStarV { get { return ((SizeTypeV & LayoutTimeSizeType.Star) != 0); } }
internal bool IsAutoV { get { return ((SizeTypeV & LayoutTimeSizeType.Auto) != 0); } }
}
///
/// Helper class for representing a key for a span in hashtable.
///
private class SpanKey
{
///
/// Constructor.
///
/// Starting index of the span.
/// Span count.
/// true for columns; false for rows.
internal SpanKey(int start, int count, bool u)
{
_start = start;
_count = count;
_u = u;
}
///
///
///
public override int GetHashCode()
{
int hash = (_start ^ (_count << 2));
if (_u) hash &= 0x7ffffff;
else hash |= 0x8000000;
return (hash);
}
///
///
///
public override bool Equals(object obj)
{
SpanKey sk = obj as SpanKey;
return ( sk != null
&& sk._start == _start
&& sk._count == _count
&& sk._u == _u );
}
///
/// Returns start index of the span.
///
internal int Start { get { return (_start); } }
///
/// Returns span count.
///
internal int Count { get { return (_count); } }
///
/// Returns true if this is a column span.
/// false if this is a row span.
///
internal bool U { get { return (_u); } }
private int _start;
private int _count;
private bool _u;
}
///
/// SpanPreferredDistributionOrderComparer.
///
private class SpanPreferredDistributionOrderComparer : IComparer
{
public int Compare(object x, object y)
{
if (x == y) return (0);
DefinitionBase definitionX = (DefinitionBase)x;
DefinitionBase definitionY = (DefinitionBase)y;
if (definitionX.UserSize.IsAuto)
{
if (definitionY.UserSize.IsAuto) return ((definitionX.MinSize > definitionY.MinSize) ? 1 : -1);
return (-1);
}
else
{
if (!definitionY.UserSize.IsAuto) return ((definitionX.PreferredSize > definitionY.PreferredSize) ? 1 : -1);
return (1);
}
}
}
///
/// SpanMaxDistributionOrderComparer.
///
private class SpanMaxDistributionOrderComparer : IComparer
{
public int Compare(object x, object y)
{
if (x == y) return (0);
DefinitionBase definitionX = (DefinitionBase)x;
DefinitionBase definitionY = (DefinitionBase)y;
if (definitionX.UserSize.IsAuto)
{
if (definitionY.UserSize.IsAuto) return ((definitionX.SizeCache > definitionY.SizeCache) ? 1 : -1);
return (1);
}
else
{
if (!definitionY.UserSize.IsAuto) return ((definitionX.SizeCache > definitionY.SizeCache) ? 1 : -1);
return (-1);
}
}
}
///
/// StarDistributionOrderComparer.
///
private class StarDistributionOrderComparer : IComparer
{
public int Compare(object x, object y)
{
if (x == y) return (0);
DefinitionBase definitionX = (DefinitionBase)x;
DefinitionBase definitionY = (DefinitionBase)y;
return ((definitionX.SizeCache > definitionY.SizeCache) ? 1 : -1);
}
}
///
/// DistributionOrderComparer.
///
private class DistributionOrderComparer: IComparer
{
public int Compare(object x, object y)
{
if (x == y) return (0);
DefinitionBase definitionX = (DefinitionBase)x;
DefinitionBase definitionY = (DefinitionBase)y;
return ((definitionX.SizeCache - definitionX.MinSizeForArrange) > (definitionY.SizeCache - definitionY.MinSizeForArrange) ? 1 : -1);
}
}
///
/// Implementation of a simple enumerator of grid's logical children
///
private class GridChildrenCollectionEnumeratorSimple : IEnumerator
{
internal GridChildrenCollectionEnumeratorSimple(Grid grid, bool includeChildren)
{
Debug.Assert(grid != null);
_currentEnumerator = -1;
_enumerator0 = new ColumnDefinitionCollection.Enumerator(grid.ExtData != null ? grid.ExtData.ColumnDefinitions : null);
_enumerator1 = new RowDefinitionCollection.Enumerator(grid.ExtData != null ? grid.ExtData.RowDefinitions : null);
// GridLineRenderer is NOT included into this enumerator.
_enumerator2Index = 0;
if (includeChildren)
{
_enumerator2Collection = grid.Children;
_enumerator2Count = _enumerator2Collection.Count;
}
else
{
_enumerator2Collection = null;
_enumerator2Count = 0;
}
}
public bool MoveNext()
{
while (_currentEnumerator < 3)
{
if (_currentEnumerator >= 0)
{
switch (_currentEnumerator)
{
case (0): if (_enumerator0.MoveNext()) { _currentChild = _enumerator0.Current; return (true); } break;
case (1): if (_enumerator1.MoveNext()) { _currentChild = _enumerator1.Current; return (true); } break;
case (2): if (_enumerator2Index < _enumerator2Count)
{
_currentChild = _enumerator2Collection[_enumerator2Index];
_enumerator2Index++;
return (true);
}
break;
}
}
_currentEnumerator++;
}
return (false);
}
public Object Current
{
get
{
if (_currentEnumerator == -1)
{
#pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception
throw new InvalidOperationException(SR.Get(SRID.EnumeratorNotStarted));
}
if (_currentEnumerator >= 3)
{
#pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception
throw new InvalidOperationException(SR.Get(SRID.EnumeratorReachedEnd));
}
// assert below is not true anymore since UIElementCollection allowes for null children
//Debug.Assert(_currentChild != null);
return (_currentChild);
}
}
public void Reset()
{
_currentEnumerator = -1;
_currentChild = null;
_enumerator0.Reset();
_enumerator1.Reset();
_enumerator2Index = 0;
}
private int _currentEnumerator;
private Object _currentChild;
private ColumnDefinitionCollection.Enumerator _enumerator0;
private RowDefinitionCollection.Enumerator _enumerator1;
private UIElementCollection _enumerator2Collection;
private int _enumerator2Index;
private int _enumerator2Count;
}
///
/// Helper to render grid lines.
///
internal class GridLinesRenderer : DrawingVisual
{
///
/// Static initialization
///
static GridLinesRenderer()
{
s_oddDashPen = new Pen(Brushes.Blue, c_penWidth);
DoubleCollection oddDashArray = new DoubleCollection();
oddDashArray.Add(c_dashLength);
oddDashArray.Add(c_dashLength);
s_oddDashPen.DashStyle = new DashStyle(oddDashArray, 0);
s_oddDashPen.DashCap = PenLineCap.Flat;
s_oddDashPen.Freeze();
s_evenDashPen = new Pen(Brushes.Yellow, c_penWidth);
DoubleCollection evenDashArray = new DoubleCollection();
evenDashArray.Add(c_dashLength);
evenDashArray.Add(c_dashLength);
s_evenDashPen.DashStyle = new DashStyle(evenDashArray, c_dashLength);
s_evenDashPen.DashCap = PenLineCap.Flat;
s_evenDashPen.Freeze();
}
///
/// UpdateRenderBounds.
///
/// Size of render bounds
internal void UpdateRenderBounds(Size boundsSize)
{
using (DrawingContext drawingContext = RenderOpen())
{
Grid grid = VisualTreeHelper.GetParent(this) as Grid;
if ( grid == null
|| grid.ShowGridLines == false )
{
return;
}
for (int i = 1; i < grid.DefinitionsU.Length; ++i)
{
DrawGridLine(
drawingContext,
grid.DefinitionsU[i].FinalOffset, 0.0,
grid.DefinitionsU[i].FinalOffset, boundsSize.Height);
}
for (int i = 1; i < grid.DefinitionsV.Length; ++i)
{
DrawGridLine(
drawingContext,
0.0, grid.DefinitionsV[i].FinalOffset,
boundsSize.Width, grid.DefinitionsV[i].FinalOffset);
}
}
}
///
/// Draw single hi-contrast line.
///
private static void DrawGridLine(
DrawingContext drawingContext,
double startX,
double startY,
double endX,
double endY)
{
Point start = new Point(startX, startY);
Point end = new Point(endX, endY);
drawingContext.DrawLine(s_oddDashPen, start, end);
drawingContext.DrawLine(s_evenDashPen, start, end);
}
private const double c_dashLength = 4.0; //
private const double c_penWidth = 1.0; //
private static readonly Pen s_oddDashPen; // first pen to draw dash
private static readonly Pen s_evenDashPen; // second pen to draw dash
private static readonly Point c_zeroPoint = new Point(0, 0);
}
#endregion Private Structures Classes
//-----------------------------------------------------
//
// Extended debugging for grid
//
//------------------------------------------------------
#if GRIDPARANOIA
private static double _performanceFrequency;
private static readonly bool _performanceFrequencyInitialized = InitializePerformanceFrequency();
//CASRemoval:[System.Security.SuppressUnmanagedCodeSecurity, System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
//CASRemoval:[System.Security.SuppressUnmanagedCodeSecurity, System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
private static double CostInMilliseconds(long count)
{
return ((double)count / _performanceFrequency);
}
private static long Cost(long startCount, long endCount)
{
long l = endCount - startCount;
if (l < 0) { l += long.MaxValue; }
return (l);
}
private static bool InitializePerformanceFrequency()
{
long l;
QueryPerformanceFrequency(out l);
_performanceFrequency = (double)l * 0.001;
return (true);
}
private struct Counter
{
internal long Start;
internal long Total;
internal int Calls;
}
private Counter[] _counters;
private bool _hasNewCounterInfo;
#endif // GRIDPARANOIA
//
// This property
// 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject
// 2. This is a performance optimization
//
internal override int EffectiveValuesInitialSize
{
get { return 9; }
}
[Conditional("GRIDPARANOIA")]
internal void EnterCounterScope(Counters scopeCounter)
{
#if GRIDPARANOIA
if (ID == "CountThis")
{
if (_counters == null)
{
_counters = new Counter[(int)Counters.Count];
}
ExitCounterScope(Counters.Default);
EnterCounter(scopeCounter);
}
else
{
_counters = null;
}
#endif // GRIDPARANOIA
}
[Conditional("GRIDPARANOIA")]
internal void ExitCounterScope(Counters scopeCounter)
{
#if GRIDPARANOIA
if (_counters != null)
{
if (scopeCounter != Counters.Default)
{
ExitCounter(scopeCounter);
}
if (_hasNewCounterInfo)
{
string NFormat = "F6";
Console.WriteLine(
"\ncounter name | total t (ms) | # of calls | per call t (ms)"
+ "\n----------------------+---------------+---------------+----------------------" );
for (int i = 0; i < _counters.Length; ++i)
{
if (_counters[i].Calls > 0)
{
Counters counter = (Counters)i;
double total = CostInMilliseconds(_counters[i].Total);
double single = total / _counters[i].Calls;
string counterName = counter.ToString();
string separator;
if (counterName.Length < 8) { separator = "\t\t\t"; }
else if (counterName.Length < 16) { separator = "\t\t"; }
else { separator = "\t"; }
Console.WriteLine(
counter.ToString() + separator
+ total.ToString(NFormat) + "\t"
+ _counters[i].Calls + "\t\t"
+ single.ToString(NFormat));
_counters[i] = new Counter();
}
}
}
_hasNewCounterInfo = false;
}
#endif // GRIDPARANOIA
}
[Conditional("GRIDPARANOIA")]
internal void EnterCounter(Counters counter)
{
#if GRIDPARANOIA
if (_counters != null)
{
Debug.Assert((int)counter < _counters.Length);
int i = (int)counter;
QueryPerformanceCounter(out _counters[i].Start);
}
#endif // GRIDPARANOIA
}
[Conditional("GRIDPARANOIA")]
internal void ExitCounter(Counters counter)
{
#if GRIDPARANOIA
if (_counters != null)
{
Debug.Assert((int)counter < _counters.Length);
int i = (int)counter;
long l;
QueryPerformanceCounter(out l);
l = Cost(_counters[i].Start, l);
_counters[i].Total += l;
_counters[i].Calls++;
_hasNewCounterInfo = true;
}
#endif // GRIDPARANOIA
}
internal enum Counters : int
{
Default = -1,
MeasureOverride,
_ValidateColsStructure,
_ValidateRowsStructure,
_ValidateCells,
_MeasureCell,
__MeasureChild,
_CalculateDesiredSize,
ArrangeOverride,
_SetFinalSize,
_ArrangeChildHelper2,
_PositionCell,
Count,
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- EditingMode.cs
- HtmlControlDesigner.cs
- DefaultValueTypeConverter.cs
- ProfileGroupSettings.cs
- ConfigXmlSignificantWhitespace.cs
- XmlSchemaGroupRef.cs
- StateDesigner.LayoutSelectionGlyph.cs
- VersionedStreamOwner.cs
- XmlUtf8RawTextWriter.cs
- Formatter.cs
- XmlSchemaObjectTable.cs
- EntitySqlQueryCacheEntry.cs
- ProgressBarRenderer.cs
- CodeLabeledStatement.cs
- ConnectionProviderAttribute.cs
- XmlSchemaAll.cs
- PixelFormatConverter.cs
- AtomServiceDocumentSerializer.cs
- SystemIPv4InterfaceProperties.cs
- XamlReader.cs
- GeometryConverter.cs
- MatrixKeyFrameCollection.cs
- XmlToDatasetMap.cs
- ServerIdentity.cs
- DataGridColumnHeaderAutomationPeer.cs
- StaticResourceExtension.cs
- ListCommandEventArgs.cs
- SiteIdentityPermission.cs
- MergeFilterQuery.cs
- DefaultMergeHelper.cs
- ToolStripPanelRenderEventArgs.cs
- HttpModuleCollection.cs
- SizeF.cs
- AdPostCacheSubstitution.cs
- HttpCookiesSection.cs
- SecurityRuntime.cs
- ObjRef.cs
- RC2.cs
- ColorContext.cs
- LinearGradientBrush.cs
- DocumentReference.cs
- CompositeKey.cs
- NetworkCredential.cs
- PackWebRequestFactory.cs
- compensatingcollection.cs
- ParseElement.cs
- XmlText.cs
- Container.cs
- Camera.cs
- FormViewInsertedEventArgs.cs
- EntityStoreSchemaGenerator.cs
- DetailsViewPagerRow.cs
- DataBoundControlHelper.cs
- StrongName.cs
- PolyBezierSegment.cs
- Wildcard.cs
- SymmetricKey.cs
- HttpCapabilitiesBase.cs
- KeyEvent.cs
- TreeNodeSelectionProcessor.cs
- XmlSchemaImport.cs
- TextRangeProviderWrapper.cs
- Rect3D.cs
- ActivityCollectionMarkupSerializer.cs
- AlternateViewCollection.cs
- MobileSysDescriptionAttribute.cs
- PathGeometry.cs
- XmlNullResolver.cs
- Visual3D.cs
- BinaryHeap.cs
- DurableInstanceProvider.cs
- Timer.cs
- XPathDescendantIterator.cs
- XPathNode.cs
- MenuScrollingVisibilityConverter.cs
- SqlDataRecord.cs
- EnterpriseServicesHelper.cs
- ListViewItemSelectionChangedEvent.cs
- GenericIdentity.cs
- BCLDebug.cs
- ListSortDescriptionCollection.cs
- NetSectionGroup.cs
- FontConverter.cs
- Interop.cs
- NavigationEventArgs.cs
- TouchEventArgs.cs
- Reference.cs
- NativeCppClassAttribute.cs
- EastAsianLunisolarCalendar.cs
- ServiceBehaviorElementCollection.cs
- DispatchChannelSink.cs
- Thread.cs
- EventProperty.cs
- MasterPageParser.cs
- ResourceManagerWrapper.cs
- HttpFileCollectionBase.cs
- Vector3DConverter.cs
- Win32.cs
- WindowsPrincipal.cs
- assemblycache.cs