private void AssignProportions() { double assignedProportion = 0; int unassignedProportions = 0; foreach (Control element in GetChildren()) { if (!(element is ProportionalStackPanelSplitter)) { var proportion = ProportionalStackPanelSplitter.GetProportion(element); if (double.IsNaN(proportion)) { unassignedProportions++; } else { assignedProportion += proportion; } } } if (unassignedProportions > 0) { var toAssign = assignedProportion; foreach (Control element in GetChildren().Where(c => double.IsNaN(ProportionalStackPanelSplitter.GetProportion(c)))) { if (!(element is ProportionalStackPanelSplitter)) { ProportionalStackPanelSplitter.SetProportion(element, (1.0 - toAssign) / unassignedProportions); assignedProportion += (1.0 - toAssign) / unassignedProportions; } } } if (assignedProportion < 1) { var numChildren = (double)GetChildren().Where(c => !(c is ProportionalStackPanelSplitter)).Count(); var toAdd = (1.0 - assignedProportion) / numChildren; foreach (var child in GetChildren().Where(c => !(c is ProportionalStackPanelSplitter))) { ProportionalStackPanelSplitter.SetProportion(child, ProportionalStackPanelSplitter.GetProportion(child) + toAdd); } } else if (assignedProportion > 1) { var numChildren = (double)GetChildren().Where(c => !(c is ProportionalStackPanelSplitter)).Count(); var toRemove = (assignedProportion - 1.0) / numChildren; foreach (var child in GetChildren().Where(c => !(c is ProportionalStackPanelSplitter))) { ProportionalStackPanelSplitter.SetProportion(child, ProportionalStackPanelSplitter.GetProportion(child) - toRemove); } } }
/// <inheritdoc/> protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) { base.ChildrenChanged(sender, e); switch (e.Action) { case NotifyCollectionChangedAction.Add: foreach (var item in e.NewItems.OfType <IControl>()) { ProportionalStackPanelSplitter.SetProportion(item as IControl, double.NaN); } break; } }
/// <inheritdoc/> protected override Size ArrangeOverride(Size arrangeSize) { double left = 0.0; double top = 0.0; double right = 0.0; double bottom = 0.0; var splitterThickness = GetTotalSplitterThickness(); // Arrange each of the Children var children = GetChildren(); int index = 0; foreach (var element in children) { // Determine the remaining space left to arrange the element Rect remainingRect = new Rect( left, top, Math.Max(0.0, arrangeSize.Width - left - right), Math.Max(0.0, arrangeSize.Height - top - bottom)); // Trim the remaining Rect to the docked size of the element // (unless the element should fill the remaining space because // of LastChildFill) if (index < children.Count()) { var desiredSize = element.DesiredSize; var proportion = ProportionalStackPanelSplitter.GetProportion(element); switch (Orientation) { case Orientation.Horizontal: if (element is ProportionalStackPanelSplitter) { left += desiredSize.Width; remainingRect = remainingRect.WithWidth(desiredSize.Width); } else { remainingRect = remainingRect.WithWidth(Math.Max(0, (arrangeSize.Width - splitterThickness) * proportion)); left += Math.Max(0, (arrangeSize.Width - splitterThickness) * proportion); } break; case Orientation.Vertical: if (element is ProportionalStackPanelSplitter) { top += desiredSize.Height; remainingRect = remainingRect.WithHeight(desiredSize.Height); } else { remainingRect = remainingRect.WithHeight(Math.Max(0, (arrangeSize.Height - splitterThickness) * proportion)); top += Math.Max(0, (arrangeSize.Height - splitterThickness) * proportion); } break; } } element.Arrange(remainingRect); index++; } return(arrangeSize); }
/// <inheritdoc/> protected override Size MeasureOverride(Size constraint) { if (constraint == Size.Infinity) { throw new Exception("Proportional StackPanel cannot be inside a component of unlimited size (like scrollviewer or listbox)"); } double usedWidth = 0.0; double usedHeight = 0.0; double maximumWidth = 0.0; double maximumHeight = 0.0; var splitterThickness = GetTotalSplitterThickness(); AssignProportions(); // Measure each of the Children foreach (Control element in GetChildren()) { // Get the child's desired size Size remainingSize = new Size( Math.Max(0.0, constraint.Width - usedWidth - splitterThickness), Math.Max(0.0, constraint.Height - usedHeight - splitterThickness)); var proportion = ProportionalStackPanelSplitter.GetProportion(element); if (!(element is ProportionalStackPanelSplitter)) { switch (Orientation) { case Orientation.Horizontal: element.Measure(constraint.WithWidth(Math.Max(0, (constraint.Width - splitterThickness) * proportion))); break; case Orientation.Vertical: element.Measure(constraint.WithHeight(Math.Max(0, (constraint.Height - splitterThickness) * proportion))); break; } } else { element.Measure(remainingSize); } var desiredSize = element.DesiredSize; // Decrease the remaining space for the rest of the children switch (Orientation) { case Orientation.Horizontal: maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height); if (element is ProportionalStackPanelSplitter) { usedWidth += desiredSize.Width; } else { usedWidth += Math.Max(0, (constraint.Width - splitterThickness) * proportion); } break; case Orientation.Vertical: maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width); if (element is ProportionalStackPanelSplitter) { usedHeight += desiredSize.Height; } else { usedHeight += Math.Max(0, (constraint.Height - splitterThickness) * proportion); } break; } } maximumWidth = Math.Max(maximumWidth, usedWidth); maximumHeight = Math.Max(maximumHeight, usedHeight); return(new Size(maximumWidth, maximumHeight)); }