public override Size MeasureDesiredSize(Size availableSize) { // Cleanse our child information first ConsolidateChildInformation(); // First step let's sum up all of our row/column constant and star height/widths. Whilst we're // doing this we we can set the actual widths/heights of constantly defined height columns and rows. double heightConstants = 0; double heightStars = 0; double widthConstants = 0; double widthStars = 0; for (int i = 0; i < (RowDefinitions.Count < 1 ? 1 : RowDefinitions.Count); i++) { RowDefinition rd = GetRow(i); if (rd.Height.Type == GridLengthType.Pixel) { double originalHeightConstants = heightConstants; heightConstants += rd.Height.Length; if (heightConstants <= availableSize.Height) { rd.ActualHeight = rd.Height.Length; } else if (originalHeightConstants <= availableSize.Height) { rd.ActualHeight = heightConstants - originalHeightConstants; } else { rd.ActualHeight = 0; } } else { rd.ActualHeight = double.NaN; if (rd.Height.Type == GridLengthType.Star) { heightStars += rd.Height.Length; } } } for (int i = 0; i < (ColumnDefinitions.Count < 1 ? 1 : ColumnDefinitions.Count); i++) { ColumnDefinition cd = GetColumn(i); if (cd.Width.Type == GridLengthType.Pixel) { double originalWidthConstants = widthConstants; widthConstants += cd.Width.Length; if (widthConstants <= availableSize.Width) { cd.ActualWidth = cd.Width.Length; } else if (originalWidthConstants <= availableSize.Width) { cd.ActualWidth = widthConstants - originalWidthConstants; } else { cd.ActualWidth = 0; } } else { cd.ActualWidth = double.NaN; if (cd.Width.Type == GridLengthType.Star) { widthStars += cd.Width.Length; } } } // Work out height/widths for autos and treat stars the same for measure except we don't actually set an actual width/height (or deduct from available) for autos for now double remainingHeight = availableSize.Height - heightConstants; double remainingWidth = availableSize.Width - widthConstants; for (int rowNumber = 0; rowNumber < (RowDefinitions.Count < 1 ? 1 : RowDefinitions.Count); rowNumber++) { RowDefinition rd = GetRow(rowNumber); for (int columnNumber = 0; columnNumber < (ColumnDefinitions.Count < 1 ? 1 : ColumnDefinitions.Count); columnNumber++) { ColumnDefinition cd = GetColumn(columnNumber); // Skip this cell if both RD/CD are constant if (rd.Height.Type == GridLengthType.Pixel && cd.Width.Type == GridLengthType.Pixel) { continue; } // Skip this cell if no children Control[] cellChildren = _childInformation.Where(ci => (ci.Row == rowNumber || (ci.Row >= RowDefinitions.Count && rowNumber == RowDefinitions.Count - 1)) && (ci.Column == columnNumber || (ci.Column >= ColumnDefinitions.Count && columnNumber == ColumnDefinitions.Count - 1))).Select(ci => ci.Child).ToArray(); if (cellChildren.Length < 1) { continue; } // Determine available size for this cell (factoring in any pre-calculated dimensions) Size cellAvailableSize = new Size(remainingWidth, remainingHeight); if (cd.Width.Type == GridLengthType.Pixel) { cellAvailableSize.Width = cd.ActualWidth; } else if (!double.IsNaN(cd.ActualWidth)) { cellAvailableSize.Width += cd.ActualWidth; } if (rd.Height.Type == GridLengthType.Pixel) { cellAvailableSize.Height = rd.ActualHeight; } else if (!double.IsNaN(rd.ActualHeight)) { cellAvailableSize.Height += rd.ActualHeight; } // Now for this particular cell ask each child to measure and find biggest dimensions Size biggestCellChildSize = new Size(0, 0); foreach (Control cellChild in cellChildren) { // For this measure we first swap-out any stretch alignments bool wasStretchHorizontal = cellChild.HorizontalAlignment == HorizontalAlignment.Stretch; bool wasStretchVertical = cellChild.VerticalAlignment == VerticalAlignment.Stretch; if (wasStretchHorizontal) { cellChild.HorizontalAlignment = HorizontalAlignment.Left; } if (wasStretchVertical) { cellChild.VerticalAlignment = VerticalAlignment.Top; } // Now perform the measure Size cellChildSize = cellChild.MeasureDesiredSize(cellAvailableSize); cellChildSize.Width += cellChild.Margin.Left + cellChild.Margin.Right; cellChildSize.Height += cellChild.Margin.Top + cellChild.Margin.Bottom; if (cellChildSize.Width > biggestCellChildSize.Width) { biggestCellChildSize.Width = cellChildSize.Width; } if (cellChildSize.Height > biggestCellChildSize.Height) { biggestCellChildSize.Height = cellChildSize.Height; } // Reset stretch if required if (wasStretchHorizontal) { cellChild.HorizontalAlignment = HorizontalAlignment.Stretch; } if (wasStretchVertical) { cellChild.VerticalAlignment = VerticalAlignment.Stretch; } } // Update the row/column definitions to have actual height/width if currently and determine offset here from already calculated and // update the remaining width/height accordingly if (cd.Width.Type == GridLengthType.Auto && (double.IsNaN(cd.ActualWidth) || biggestCellChildSize.Width > cd.ActualWidth)) { double originalWidth = double.IsNaN(cd.ActualWidth) ? 0 : cd.ActualWidth; cd.ActualWidth = biggestCellChildSize.Width; double widthDifference = cd.ActualWidth - originalWidth; remainingWidth -= widthDifference; } if (rd.Height.Type == GridLengthType.Auto && (double.IsNaN(rd.ActualHeight) || biggestCellChildSize.Height > rd.ActualHeight)) { double originalHeight = double.IsNaN(rd.ActualHeight) ? 0 : rd.ActualHeight; rd.ActualHeight = biggestCellChildSize.Height; double HeightDifference = rd.ActualHeight - originalHeight; remainingHeight -= HeightDifference; } } } // Now distribute stars accordingly - currently these cells will have been fit by auto. We should now look at all remaining space and divvy it up // accordingly for star cells if (widthStars > 0) { double widthPerStar = remainingWidth / widthStars; for (int columnNumber = 0; columnNumber < (ColumnDefinitions.Count < 1 ? 1 : ColumnDefinitions.Count); columnNumber++) { ColumnDefinition cd = GetColumn(columnNumber); if (cd.Width.Type != GridLengthType.Star) { continue; } double starSize = widthPerStar * cd.Width.Length; if (double.IsNaN(cd.ActualWidth) || starSize > cd.ActualWidth) { cd.ActualWidth = starSize; } } remainingWidth = 0; } if (heightStars > 0) { double heightPerStar = remainingHeight / heightStars; for (int rowNumber = 0; rowNumber < (RowDefinitions.Count < 1 ? 1 : RowDefinitions.Count); rowNumber++) { RowDefinition rd = GetRow(rowNumber); if (rd.Height.Type != GridLengthType.Star) { continue; } double starSize = heightPerStar * rd.Height.Length; if (double.IsNaN(rd.ActualHeight) || starSize > rd.ActualHeight) { rd.ActualHeight = starSize; } } remainingHeight = 0; } // All actual height/widths for columsn and rows are calculated so lets store our summary of these to use in render double offsetRow = 0; double offsetColumn = 0; for (int rowNumber = 0; rowNumber < (RowDefinitions.Count < 1 ? 1 : RowDefinitions.Count); rowNumber++) { RowDefinition rd = GetRow(rowNumber); rd.ActualOffset = offsetRow; offsetRow += rd.ActualHeight; } for (int columnNumber = 0; columnNumber < (ColumnDefinitions.Count < 1 ? 1 : ColumnDefinitions.Count); columnNumber++) { ColumnDefinition cd = GetColumn(columnNumber); cd.ActualOffset = offsetColumn; offsetColumn += cd.ActualWidth; } // For each child we can now put in widths/heights as required - do this in to their Measure() and set actual size int zIndex = 0; for (int rowNumber = 0; rowNumber < (RowDefinitions.Count < 1 ? 1 : RowDefinitions.Count); rowNumber++) { RowDefinition rd = GetRow(rowNumber); for (int columnNumber = 0; columnNumber < (ColumnDefinitions.Count < 1 ? 1 : ColumnDefinitions.Count); columnNumber++) { ColumnDefinition cd = GetColumn(columnNumber); // Tell the child its actual size that we'll be using Control[] cellChildren = _childInformation.Where(ci => (ci.Row == rowNumber || (ci.Row >= RowDefinitions.Count && rowNumber == RowDefinitions.Count - 1)) && (ci.Column == columnNumber || (ci.Column >= ColumnDefinitions.Count && columnNumber == ColumnDefinitions.Count - 1))).Select(ci => ci.Child).ToArray(); foreach (Control child in cellChildren) { // Determine available size for child minus its margins Size measureSize = new Size(cd.ActualWidth, rd.ActualHeight); measureSize.Width -= child.Margin.Left; measureSize.Width -= child.Margin.Right; measureSize.Height -= child.Margin.Top; measureSize.Height -= child.Margin.Bottom; // Get the child to measure Size childActualSize = child.MeasureDesiredSize(measureSize); // Cap measurement at appropriate max bounds and allow for stretch if (childActualSize.Width > measureSize.Width) { childActualSize.Width = measureSize.Width; } if (childActualSize.Height > measureSize.Height) { childActualSize.Height = measureSize.Height; } if (child.HorizontalAlignment == HorizontalAlignment.Stretch) { childActualSize.Width = measureSize.Width; } if (child.VerticalAlignment == VerticalAlignment.Stretch) { childActualSize.Height = measureSize.Height; } // Give the child the space required child.SetActualSize(childActualSize); // Setup layout information double x = cd.ActualOffset; double y = rd.ActualOffset; switch (child.HorizontalAlignment) { case HorizontalAlignment.Stretch: case HorizontalAlignment.Left: x += child.Margin.Left; break; case HorizontalAlignment.Right: x += cd.ActualWidth - child.Margin.Right - childActualSize.Width; break; case HorizontalAlignment.Middle: x += (cd.ActualWidth / 2) - ((child.Margin.Left + child.Margin.Right + childActualSize.Width) / 2); break; default: throw new Exception("Unsupported horizontal alignment"); } switch (child.VerticalAlignment) { case VerticalAlignment.Stretch: case VerticalAlignment.Top: y += child.Margin.Top; break; case VerticalAlignment.Bottom: y += rd.ActualHeight - child.Margin.Bottom - childActualSize.Height; break; case VerticalAlignment.Middle: y += (rd.ActualHeight / 2) - ((child.Margin.Top + child.Margin.Bottom + childActualSize.Height) / 2); break; default: throw new Exception("Unsupported vertical alignment"); } LayoutInformation layoutInformation = GetLayoutInformationForChild(child); layoutInformation.Rectangle = new Rectangle(x, y, child.ActualWidth, child.ActualHeight); layoutInformation.ZIndex = zIndex++; } } } // Finally we return the size we will be using return(new Size(availableSize.Width - remainingWidth, availableSize.Height - remainingHeight)); }
public override Size MeasureDesiredSize(Size availableSize) { // If we have no children this one is easy if (Child == null) { return(HorizontalAlignment == HorizontalAlignment.Stretch && VerticalAlignment == VerticalAlignment.Stretch ? availableSize : new Size(0, 0)); } // Determine actual available size for child by taking off our own internal spacings (i.e. borders etc) double availableWidth = availableSize.Width - Child.Margin.Left - Child.Margin.Right - InternalSpacing.Left - InternalSpacing.Right; double availableHeight = availableSize.Height - Child.Margin.Top - Child.Margin.Bottom - InternalSpacing.Top - InternalSpacing.Bottom; // Perform a measure pass on this child and set its size Size childSize = Child.MeasureDesiredSize(new Size(availableWidth, availableHeight)); Child.SetActualSize(childSize); // Decide the width we will use Size finalSizeToUse = new Size(childSize.Width + InternalSpacing.Left + InternalSpacing.Right + Child.Margin.Left + Child.Margin.Right, childSize.Height + InternalSpacing.Top + InternalSpacing.Bottom + Child.Margin.Top + Child.Margin.Bottom); if (HorizontalAlignment == HorizontalAlignment.Stretch) { finalSizeToUse.Width = availableSize.Width; } if (VerticalAlignment == VerticalAlignment.Stretch) { finalSizeToUse.Height = availableSize.Height; } // Now we have the desired sizing information we can setup its layout information for painting later on double x; switch (Child.HorizontalAlignment) { case HorizontalAlignment.Stretch: case HorizontalAlignment.Left: x = Child.Margin.Left + InternalSpacing.Left; break; case HorizontalAlignment.Right: x = finalSizeToUse.Width - Child.Margin.Right - childSize.Width - InternalSpacing.Right; break; case HorizontalAlignment.Middle: x = (finalSizeToUse.Width / 2) - ((Child.Margin.Left + Child.Margin.Right + childSize.Width + InternalSpacing.Left + InternalSpacing.Right) / 2); break; default: throw new Exception("Unsupported horizontal alignment"); } if (x < 0) { x = 0; } if (x > finalSizeToUse.Width) { x = finalSizeToUse.Width; } double y; switch (Child.VerticalAlignment) { case VerticalAlignment.Stretch: case VerticalAlignment.Top: y = Child.Margin.Top + InternalSpacing.Top; break; case VerticalAlignment.Bottom: y = finalSizeToUse.Height - Child.Margin.Bottom - childSize.Height - InternalSpacing.Bottom; break; case VerticalAlignment.Middle: y = (finalSizeToUse.Height / 2) - ((Child.Margin.Top + Child.Margin.Bottom + childSize.Height + InternalSpacing.Top + InternalSpacing.Bottom) / 2); break; default: throw new Exception("Unsupported vertical alignment"); } if (y < 0) { y = 0; } if (y > finalSizeToUse.Height) { y = finalSizeToUse.Height; } LayoutInformation layoutInformation = GetLayoutInformationForChild(Child); layoutInformation.Rectangle = new Rectangle(x, y, childSize.Width, childSize.Height); layoutInformation.ZIndex = 0; // Return size including our borders and child margins factoring in any stretch return(finalSizeToUse); }