Example #1
0
        /// <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);
            }
        }