Constrained StackPanel

|

I’ve been spending a little time today over on stackoverflow.com and just answered a question about constraining the width of a StackPanel, and I thought I’d share my solution here too.

The question in its entirety was:

How can I constrain a vertical WPF StackPanel's width to the most narrow item it contains. The StackPanel's width must not be greater than the width of any other child element.

My initial thought to try and use a simple IValueConverter implementation; hand the in the child collection at the right time and bingo! constrain the size of the panel. However, that proved to be a little problematic as due to timing and data binding issues. One answer already suggested was to create a custom panel (which had no up votes for some reason – go figure); which would have been my next choice after the converter.

So I set about knocking up a ConstrainedStackPanel, which looks something like this:

public class ConstrainedStackPanel : StackPanel
{
public ConstrainedStackPanel()
{
}

protected override Size MeasureOverride(Size constraint)
{
foreach (var item in this.Children)
{
FrameworkElement element = item as FrameworkElement;
if (element != null)
constraint.Width = Math.Min(element.Width, constraint.Width);
}

return base.MeasureOverride(constraint);
}

protected override Size ArrangeOverride(Size arrangeSize)
{
foreach (var item in this.Children)
{
FrameworkElement element = item as FrameworkElement;
if (element != null)
arrangeSize.Width = Math.Min(element.Width, arrangeSize.Width);
}

return base.ArrangeOverride(arrangeSize);
}
}

Here I’m simply enumerating the child collection at the right time, being a good WPF citizen and overriding both Measure and Arrange to get the desired effect, by keeping the smallest Width value at all times during the two stage rendering process. To consume the new panel in XAML, simply use it like any other StackPanel, as shown here:

<l:ConstrainedStackPanel x:Name="panelB">
    <Button Width="100" Content="100" />
    <Button Width="200" Content="200" />
    <Button Width="300" Content="300" />
    <Button Width="400" Content="400" />
</l:ConstrainedStackPanel>

When compared with a regular StackPanel the results look like the following screenshot:

WidthStackPanel

And there you have it. In terms of requirements for a custom panel they don’t get much simpler than that, so it was a pretty straight-forward solution, but I hope it helps someone.

To follow the progress of the question and to see what other people come up with here’s a full link to the question: http://stackoverflow.com/questions/377523/constrain-stackpanels-width-to-smallest-child-element.

Comments and questions welcome as always.

2 comments:

Derek Lakin said...

Presumably just a simple extension to store the smallest height for an Orientation of Horizontal, instead of the default Vertical?

Paul said...

Absolutely right. All the information needed to make that decision should be available at that time in the rendering process.

Good question, thanks!

-PJ