Here’s a new panel to add to your tool chest. It’s one I expect to use almost daily and save approximately 3.1 hours of typing over the next year. Hopefully you’ll find it useful as useful as I think it is.
What’s wrong with the built-in StackPanel:
You can’t get children to conform to the size of the panel. Imagine you want the following label and list but want the height of the Listbox to stretch or shrink according to the room available:
At first glance, a StackPanel would seem to be the natural choice for which panel to accomplish this. Afterall, you just want a linear list of two objects: a TextBlock and ListBox. However, unless you are willing to hardcode the size of the ListBox or resize it with code, with a StackPanel you’ll end up with:
Yuck! Clipping instead of a scrollbar. So how do you get the desired behavior?
Answer: Grid.
So what’s wrong with a Grid?
- Grids take more lines xaml to create. Compare the StackPanel Xaml to the Grid’s above and notice the 4 extra lines of Xaml. Ok, not a huge deal but if you’re doing this every day like me, you start to get sick of creating RowDefinition and ColumnDefinition objects.
- With a grid you have to explicitly define the order of the children. Adding the attached property Grid.Row is easy on your first pass but what happens if you want to insert a new row somewhere in the middle. You not only have to add a new RowDefinition, but you also have to remember to go increment the row number on all subsequent siblings. This is a pain and error prone because a sibling would potentially be defined in any order in the grid.
Wouldn’t it be great if you could get the best of both worlds?
What we really want here is a StackPanel that allows you to define the size of the children.
Welcome SuperStackPanel:
Just set the attached property StackPanel.Length to “*” to get a child to stretch.
In fact, SuperStackPanel lets you use all the same GridLength values you can use in a Grid (Auto, *, n*, and x):
Have I sold you yet? The nice part is that SuperStackPanel is completely compatible with a normal StackPanel. Just swap out your StackPanel for SuperStackPanel and everything will look the same because the default value of Length is “Auto”. Frankly, I’d love to see Microsoft expand the role of StackPanel to support this natively. Of course, until then, there’s SuperStackPanel.
SuperStackPanel is lightweight and simple to add to your project, just download the C# source code, add to your project, add the namespace to your xaml and away you’ll go. Enjoy!
Excellent idea. The only thing I don’t like is theat this is a custom container. I’d rather implement it as an attached property to a child of a regular grid – something like
Oops the blog didn’t like the markup…
Grid
TextBlock my:gridextenssions.Height=”Auto”
Why not use a DockPanel? This is exactly the scenario it was meant for.
Great point Rob, a DockPanel can accomplish most of this too but with a few disadvantages: 1) You have to include the SL toolkit and 2) You can only stretch one item. As far as I know there is no way to share the remaining space between 2 or more children… or to implement fractional spacing via 2*…etc
Additionally, I find the resulting Xaml more readable with SuperStackPanel but that is just my preference. DocPanel is certainly a reasonable alternative just like Grid.
Well, we almost always use the Toolkit on projects, so that’s not a big deal to me. I probably would use a DockPanel with two children: a border and a grid to accomplish the spacing issues. I admit, your technique is much easier on the eyes which probably makes it a superior solution.
[…] SuperStackPanel = Grid + StackPanel […]
I came across the same issue (I think – I approached it from the issue of ItemTemplates), but resolved it slightly differently: I created a behaviour that I attached to the ItemTemplate
With the behaviour in the project, it’s even picked up by Blend so the designers on our project can add it to the items they want.
One of these days when I get enough time to blog about things, I’ll put it online. Could prove handy
Hello,
what am i doing wrong,
the StackPanel doesn’t complie on the following lines:
var AutoChildren = Children.Where(o => GetLength((DependencyObject)o).IsAuto);
o.DesiredSize.Height);
<- Sum() return decimal?, Probably because of var in the above line.
Thanks
Hello,
what am i doing wrong?
the StackPanel doesn’t complie on the following lines:
var AutoChildren = Children.Where(o => GetLength((DependencyObject)o).IsAuto);
o.DesiredSize.Height);
<- Sum() return decimal?, Probably because of var in the above line.
Thanks
(Retry of the post message)
what am i doing wrong?
the StackPanel doesn’t complie on the following lines:
var AutoChildren = Children.Where(o => GetLength((DependencyObject)o).IsAuto);
– No Method Where() (probably Linq) Found.
On this line. Linq seems to work, but the return type is wrong.
double AutoSize = AutoChildren.Sum(o => o.DesiredSize.Height);
– Sum() return decimal?, Probably because of var in the above line.
Thanks