Lately I’ve been making a point of being a little more organized about how I track reusable code. When I’m working with a client and come across something that is worth keeping or sharing I’ll usually ask if I can do it on my own time so I can keep or share it. So far everyone’s been very amenable to that idea. I think it’s a win-win: free code for everyone (future me, you, current clients and future clients).
So here are the first fruits of that, a few “to share” Panels (for WPF and Silverlight) that I’ve been meaning to blog about for a while. They’re pretty simple, but have proven to be a really handy part of my arsenal.
ColumnWrapPanel (WPF or Silverlight)
This is the one I’m most excited about. It’s a smarter version of a WrapPanel that tries to enforce a little more order on how it arranges its items. It does this by maintaining a sense of columns and row height as it places the items and then snaps the items to those columns and rows. Its also smart enough to place items into “empty” spaces that get created by extra tall or extra short items in the panel. Think of it as WrapPanel without the jaggies.
Here’s some XAML:
< c:columnwrappanel mincolumnwidth= "200" rowheightincrement= "50"> < rectangle height= "100" fill= "YellowGreen" c:columnwrappanel.columnspan= "3"> </ rectangle> < rectangle height= "250" fill= "Orange" c:columnwrappanel.columnspan= "2"> </ rectangle> < rectangle height= "50" fill= "LightBlue" c:columnwrappanel.columnspan= "1"> </ rectangle> < rectangle height= "50" fill= "Tomato" c:columnwrappanel.columnspan= "1"> </ rectangle> </ c:columnwrappanel>
This solves a problem that I’ve run into over and over and to which I've never had a good solution, especially when laying out a data entry form or something else where you have a set of items that have dissimilar sizing requirements but which need to fill an area efficiently. In the case of a traditional form, you could think of each color square as being a StackPanel or Grid that contains grouped elements in the form (e.g. use green for a name, orange for a set of address fields and blue and red for notes or a single checkbox).
By the way, there are some obvious efficiencies to be gained in this code (mostly with how I maintain the cell table) so if anyone finds the time to clean it up, I’d love your updated and better code!
SmartGrid (WPF Only)
This is a super simple panel that derives from Grid. It allows you to specify names for your rows and columns and then assign items by name (rather than by index). I really like this for two reasons: first, it gives you self documenting XAML and second because it allows you to add and remove columns and rows without need to reindex all the content in your panel. Here’s what the XAML looks like.
< c:smartgrid> < c:smartgrid.rowdefinitions> < c:smartrowdefinition rowname= "Header" height= "65"> </ c:smartrowdefinition> < c:smartrowdefinition rowname= "Body" height= "*"> </ c:smartrowdefinition> </ c:smartgrid.rowdefinitions> < rectangle c:smartgrid.rowname= "Header" fill= "YellowGreen"> </ rectangle> < rectangle c:smartgrid.rowname= "Body" fill= "Orange"> </ rectangle> </ c:smartgrid>
I tried to port this one from WPF to Silverlight but I couldn’t find the right hooks. Unfortunately Silverlight Grid has sealed up Measure and Arrange and the other sizing events were too late to set the indexes properly on the items. There may have been something else too, but you may succeed here where I failed. Let me know if your port it.
IndexingStackPanel (WPF Only)
This one is old school. I created it for Kaxaml originally, but continue to get a lot of mileage from it. It’s a derived StackPanel that sets attached properties on its children to indicate where they show up in the panel. I use it a lot with an ItemsControl. It tells you whether it’s odd/even (slightly less useful now that we have alternating row colors built into the framework), first/last/middle and where it is relative to the selected item in a Selector.
My favorite use case for this is dealing with rounded corners on a menu or in a list. The problem shows up if the thing that contains your list has rounded corners on the top and bottom. In that case, you want all items to be square except the top and bottom. With IndexingStackPanel you can just trigger of the attached property to find if you’re top or bottom or middle and adjust the corner radius of the item as appropriate.
By the way, if you use this with an ItemsControl, you need to create the triggers in the ItemContainerStyle (not the ItemTemplate).