/// <summary> /// Measures the grid. /// </summary> /// <param name="constraint">The available size.</param> /// <returns>The desired size of the control.</returns> protected override Size MeasureOverride(Size constraint) { // Situation 1/2: // If the grid doesn't have any column/row definitions, it behaves like a normal panel. // GridLayout supports this situation but we handle this separately for performance. if (ColumnDefinitions.Count == 0 && RowDefinitions.Count == 0) { var maxWidth = 0.0; var maxHeight = 0.0; foreach (var child in Children.OfType <Control>()) { child.Measure(constraint); maxWidth = Math.Max(maxWidth, child.DesiredSize.Width); maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); } maxWidth = Math.Min(maxWidth, constraint.Width); maxHeight = Math.Min(maxHeight, constraint.Height); return(new Size(maxWidth, maxHeight)); } // Situation 2/2: // If the grid defines some columns or rows. // Debug Tip: // - GridLayout doesn't hold any state, so you can drag the debugger execution // arrow back to any statements and re-run them without any side-effect. var measureCache = new Dictionary <Control, Size>(); var(safeColumns, safeRows) = GetSafeColumnRows(); var columnLayout = new GridLayout(ColumnDefinitions); var rowLayout = new GridLayout(RowDefinitions); // Note: If a child stays in a * or Auto column/row, use constraint to measure it. columnLayout.AppendMeasureConventions(safeColumns, child => MeasureOnce(child, constraint).Width); rowLayout.AppendMeasureConventions(safeRows, child => MeasureOnce(child, constraint).Height); // Calculate measurement. var columnResult = columnLayout.Measure(constraint.Width); var rowResult = rowLayout.Measure(constraint.Height); // Use the results of the measurement to measure the rest of the children. foreach (var child in Children.OfType <Control>()) { var(column, columnSpan) = safeColumns[child]; var(row, rowSpan) = safeRows[child]; var width = Enumerable.Range(column, columnSpan).Select(x => columnResult.LengthList[x]).Sum(); var height = Enumerable.Range(row, rowSpan).Select(x => rowResult.LengthList[x]).Sum(); MeasureOnce(child, new Size(width, height)); } // Cache the measure result and return the desired size. _columnMeasureCache = columnResult; _rowMeasureCache = rowResult; _rowLayoutCache = rowLayout; _columnLayoutCache = columnLayout; if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) { _sharedSizeHost.UpdateMeasureStatus(this, rowResult, columnResult); } return(new Size(columnResult.DesiredLength, rowResult.DesiredLength)); // Measure each child only once. // If a child has been measured, it will just return the desired size. Size MeasureOnce(Control child, Size size) { if (measureCache.TryGetValue(child, out var desiredSize)) { return(desiredSize); } child.Measure(size); desiredSize = child.DesiredSize; measureCache[child] = desiredSize; return(desiredSize); } }