Click here to Skip to main content
Click here to Skip to main content

WPF - A Constraining Stack Panel

, 11 Jun 2009
Rate this:
Please Sign up or sign in to vote.
Custom stack panel for dealing with scrollable elements

Introduction

The first column in the picture above shows how a regular StackPanel gives scrollable elements all the space they desire, which may render some elements invisible. Now, this could be fixed by using a Grid instead, but I wanted an easy interface for dynamically adding, inserting and removing elements, so I took a stab at a custom panel. The second column in the picture shows this custom panel, ConstrainingStackPanel, which is able to constrain the size of scrollable elements, so that the panel doesn't use more space than available.

Using the Code

The interface is similar to the standard StackPanel, with the exception of a custom attached property called "Constrain", which tells the panel to constrain the size of an element, if necessary. Elements which support scrolling should have this property set to true.

Currently, the panel only supports vertical orientation.

Here's the XAML for the second column in the picture above:

<my:ConstrainingStackPanel Grid.Column="1" Margin="10">
    <TextBlock Margin="5">Column 2 - ConstrainigStackPanel</TextBlock>
    <Button>a</Button>
    <ListBox my:ConstrainingStackPanel.Constrain="true" 
		ItemsSource="{StaticResource actors}"/>
    <Button>b</Button>
    <ListBox my:ConstrainingStackPanel.Constrain="true" 
		ItemsSource="{StaticResource movies}"/>
    <Button>c</Button>
</my:ConstrainingStackPanel>

Points of Interest

Most of the work is done in the panel's MeasureOverride method:

protected override Size MeasureOverride(Size availableSize)
{
    // Desired size for this panel to return to the parent
    double desiredHeight = 0;
    double desiredWidth = 0;

    // Desired heights the two 'types' of children
    double desiredHeightConstrainableChildren = 0;
    double desiredHeightRegularChildren = 0;

    _constrainableChildren.Clear();

    foreach (UIElement child in InternalChildren)
    {
        // Let child figure out how much space it needs
        child.Measure(availableSize);

        if (GetConstrain(child))
        {
            // Deal with constrainable children later once we know if they
            // need to be constrained or not
            _constrainableChildren.Add(child);
            desiredHeightConstrainableChildren += child.DesiredSize.Height;
        }
        else
        {
            desiredHeightRegularChildren += child.DesiredSize.Height;
            desiredHeight += child.DesiredSize.Height;
            desiredWidth = Math.Max(desiredWidth, child.DesiredSize.Width);
        }
    }

    // If the desired height of all children exceeds the available height, set the
    // constrain flag to true
    double desiredHeightAllChildren = 
	desiredHeightConstrainableChildren + desiredHeightRegularChildren;
    bool constrain = desiredHeightAllChildren > availableSize.Height;

    // Holds the space available for the constrainable children to share
    double availableVerticalSpace = 
	Math.Max(availableSize.Height - desiredHeightRegularChildren, 0);

    // Re-measure these children and constrain them proportionally, if necessary, so the
    // largest child gets the largest portion of the vertical space available
    foreach (UIElement child in _constrainableChildren)
    {
        if (constrain)
        {
            double percent = 
		child.DesiredSize.Height / desiredHeightConstrainableChildren;
            double verticalSpace = percent * availableVerticalSpace;
            child.Measure(new Size(availableSize.Width, verticalSpace));
        }
        desiredHeight += child.DesiredSize.Height;
        desiredWidth = Math.Max(desiredWidth, child.DesiredSize.Width);
    }

    return new Size(desiredWidth, desiredHeight);
}

The basic idea is to calculate the amount of space available for the scrollable elements. Once that space has been calculated, the space is then divided up among the scrollable elements. The division of space is done proportionally to the desired sizes of the elements.

Final Points

Currently, only vertical orientation is supported.

The panel works best when the entire size of a scrollable element is actually scrollable, so if a user control with a partial scrollable area is added to the panel, the space allocation may not be accurate. Maybe the panel's interface could be improved to expose more control over how space is allocated to scrollable elements.

Any comments or suggestions are welcome.

History

  • 9th June, 2009: Initial version
  • 11th June, 2009: Updated demo

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Torbjorn Berglund
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
QuestionGood One PinmemberLaurenceBunnage23-Nov-12 2:38 
Questionthanks Pinmemberbahman aminipour11-Jun-12 22:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140826.1 | Last Updated 11 Jun 2009
Article Copyright 2009 by Torbjorn Berglund
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid