Click here to Skip to main content
Licence CPOL
First Posted 10 Jun 2009
Views 12,763
Downloads 250
Bookmarked 16 times

WPF - A Constraining Stack Panel

By | 11 Jun 2009 | Article
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)

About the Author

Torbjorn Berglund

Software Developer (Senior)

United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 11 Jun 2009
Article Copyright 2009 by Torbjorn Berglund
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid