I’ve written a custom panel which displays its children docked either vertically or horizontally, separated by moving splitters in between. Since the Grid panel offers much of this functionality out-of-the-box, I just inherited from it.
To create the layout, upon Loaded is fired I do the following:
1) Read how many children it has and create the appropiate number of rows/colums.
2) Position every existing children in the corresponding row/colum.
3) Create, position and add a GridSplitter for every child.
This approach looks and works fine, but it opens the door to a lot of problems:
-
Since it’s added a GridSplitter for each child, there are twice the number of expected children. If someone added 3 elements to it, Children.Count would return 6.
-
User could insert/remove things at the wrong place.
-
It just throws an exception when this Grid is used as the ItemsPanel for an ItemsControl, since in this case WPF (not Silverlight) does not allow direct children manipulation.
These 3 cases are the ones I’ve already tested, but I’m pretty sure a lot more would arise depending on what the user does with it.
As it turns out, this class must be regarded as ‘implementation details’, so the real question is, what control should I put in front of the user?
It sounds like it should be a Panel, but I can’t control the Children property since it’s not virtual, and there’s also the ItemsControl which I think could be a good candidate, but I really don’t know.
I’d much appreciate any kind of advice or some directions to do this the right way.
Thanks in advance.
You see using just grid you leave yourself with an imperative way of adding items only. As in
myCustomGrid1.AddMyItem(***), Grids simply don’t have ItemsSource property. ItemsControls do – so if you need support for declarative items sources i.e. myControl.ItemsSource = {Binding …} you’re going to derive your control from ItemsControl. This is not a two liner – making your ItemsPanel Children writable is a big challange – there’s no simple way of doing that.
This is all about a small thing overlooked during the Grid’s design – splitters shouldn’t have been added to Children collection, as Children are visulaizations of your BOs while spliiters are just formatting elements.
Here’s what I would do.
Forget about ItemsSource & items altogether – it’s aint worht the hassle. The only way to add/remove items to your control will be AddResiazableItem/RemoveResizbleItem. Calls will add items and splitter (for the middle items), extend the number of rows/cols of your grid depeneding on its orientation, set Grid.Row/Grid.Column attached properties for your visual children. You can keep your actual objects internally to support Orientation change.
If at any stage you’ll want to bind your control to IEnumerable source – just create an attached behavior, which will iterate through the items and call AddResiazableItem within a loop.
Cheers.
P.S. To moderators – the editor seems to get broken, lads. I cant see the second item.
P.S.S. Got it fixed after a few tries.