I have a class
public class MyObject {
public string Caption { get; set; }
public string SomeText { get; set; }
public IList<MyObject> ChildItems { get; set; }
public Dictionary<string,Action> ItemActions { get; set; }
}
and an UltraGrid whose DataSource will be an IList<MyObject>.
IList<MyObject> list = ListRepository.GetAll<MyObject>();
MyUltraGrid.DataSource = list.
When I set an IList<MyObject> as the UltraGrid's DataSource it seems that the Dictionary property ItemActions is being chosen as the property to use to create bands in the grid and I get Key Not Found exceptions when trying to access columns in lower bands, because I'm expecting the columns in lower bands to be keyed of of other properties of MyObject.
For example:
MyGrid.DisplayLayout.Bands[1].Columns["SomeText"].PerformAutoResize();
would result in an exception because the "SomeText" key of the Columns collection doesn't exist. Band[1] gets created from the Dictionary property of MyObject instead of the IList<MyObject> property.
I have worked around this for now by changing the ItemActions property to type Object instead of a Dictionary and casting when I need to reference it, but I'd rather not have to do that.
Is there a way to force the UltraGrid binding to choose ChildItems as the hierarchy path?
Hi,
It's probably because you are using IList<T>. Using an interface instead of a concrete implementation will cause all sorts of problems when binding.Also, IList is not a great interface to use for DataBinding, anyway. It's very limited and won't support all of the notifications needed for DataBinding.
Change IList<T> to List<T> or better yet, BindingList<T> and it will work a lot better.
Ah, what another day will do for your code. It turns out that it was indeed not a problem with the properties and binding, but rather the band level that I was inspecting. I had a foreach loop that looped through the entire bands collection and the exception was being thrown on level 100. I only have 3 levels that I'm interested in so I rewrote the loop and all is well using a Dictionary property as I originally intended.
I did, however, take your advice and switch over to BindingList instead of IList. Thanks for the nudge in that direction!
Hi Shawn,
If you are binding the grid to a recursive data source (which you are) and you only care about 3 levels of data, then it would be a good idea to set the MaxBandDepth property to 3. That will get rid of all those extra bands and you won't have to worry about coding your loops to ignore them.
It will also give you a boost to performance.
That's what I'd tried originally when I ran into the problem with the Dictionary property. I've gone back to this now that all the IList properties are switched to BindingList and I'm back to the binding problem again where the bottom level of the hierarchy seems to be set by the Dictionary property.
I set MaxBandDept to 3 and when I debug, the Bands.Count property is set to 5 and the columns of the band with index 3 are "Key" and "Value". There are 3 items in the dictionary at that level, so I'm thinking that that's what's happening. The grid is trying to use the Dictionary to build the hierarchy at the bottom.
MaxBandDepth only limits the depth, not the number of bands.
In this case, your MyObject has two properties which return a child list: ChildItems and ItemActions - so they are sibling bands both of which are at a depth of 2. Since ChildItems is recursive, it has 2 child bands which are also siblings.
So 5 bands is correct.
Root band (List of MyObjects)
ChildItems
ItemActions
Ah, ok. I must have been misunderstanding the MaxBandDepth property and that's cleared up now. But that brings me back to the original question. Is there a way to specify which properties to choose for bands in the situation where there are multiple collection based properties on an hierarchical object? If not, then what I can see is either some kind of wrapper or the need to do the casting that I mentioned originally.
Excellent, that's exactly the rundown I was looking for. Thanks!
I have some thinking to do to see which way I want to go now. I'm actually creating rows with buttons but I don't want the button actions to be explicit properties of MyObject so as to not create a tight coupling situation with MyObject, thus the Dictionary<string,Action>.
I will look again at UltraTree, which I did investigate for this at one point. I don't remember exactly why I decided against it but it shouldn't take too long to either prove things out or to remember why it wouldn't work. If I come back to UltraGrid then maybe I can just create a method on MyObject that will return the dictionary and turn the field to a private member.
Thank you very, very much for your time Mike! Much appreciated!
Shawn Quillman said:Is there a way to specify which properties to choose for bands in the situation where there are multiple collection based properties on an hierarchical object?
It depends what you mean. The way it works is that the grid gets the properties of the object from the BindingManager. For each member exposed by the object (MyObject), it determines which ones are fields and which ones are child lists by checking to see if they implement IList. So anything that implements IList is treated as a child band and anything that does not is treated as a column.
So you cannot make the dictionary in this case appear in the grid as a column. But... I don't imagine you want to display this field as a column, anyway. All it would show would be the type name - it wouldn't display the entire list inside a single cell.
My guess is that what you really want here is to hide the child band, which you can do very easily using the hidden property on the band. I would use the InitializeLayout event for this and remember that the ItemActions will actually be displayed twice, so you will need to hide both bands that show it.
This still leaves you with a problem where you will need to ignore these bands when looping, but it's a little easier, since now you can simply ignore bands whose Hidden property is true.
Or, you could do what you mentioned and create some sort of wrapper. For example, you could use the UltraDataSource in On-Demand mode as a sort've intermediary between the grid and the 'real' data source.
Another alternative would be to use UltraTree instead of UltraGrid. UltraTree actually does allow you to choose whether any particular field displays as a child band or as a column. You do this by setting the IsChaptered property on the column. The down side is that the tree doesn't support exporting, filtering, printing or summaries like the grid does.
Still another option would be to derive a class from List<MyObject> and implement ITypedList. You could then override GetPropertyDescriptors and filter out the ones for the Dictionary.