I just spent a few hours diagnosing what appears to be a bug in the new ListBindingHelper class used by, among other things, the Windows Forms DataGridView control. If you call one of the GetListItemProperties methods, as the DGV does when automagically determining the columns based on your data source, it may ultimately call a helper method called GetListItemPropertiesByEnumerable. This is a last ditch attempt to determine the properties based on the type of objects in your data source.
Well, actually, it is the second choice; it will first look to see if your list class implements ITypedList and let it take over the work of getting the properties, but if your list class is one of the handy ones in the framework (an array, ArrayList, List<>, etc.), it will fall back on the GetListItemPropertiesByEnumerable method, assuming your list class implements IEnumerable, which most do (as an aside, it will then just get the properties on your list or, to be more precise, whatever the heck you passed into the method).
So the bug (or so I call it) is in that method. I presume in an effort to be all high-performing and such, the developer wrote in a killer short-cut:
Type yourEnumerableListType = yourEnumerableList.GetType();if (typeof(Array).IsAssignableFrom(yourEnumerableListType)) return TypeDescriptor.GetProperties(yourEnumerableListType.GetElementType(), ListBindingHelper.BrowsableAttributeList);
Note that I took liberties of paraphrasing here, but if you know how ICustomTypeDescriptor works, you'll readily see the problem here. You see, it works on instances, not types. This means that your implementation of ICustomTypeDescriptor will never be called if you are binding an array, even if there are valid instances in the collection upon which it could be called. I see this as a major bug, and I'm quite surprised it made it through QA.
You see, if your enumerable list class is not just a plain-Jane array, it will fall through that short-cut down to what it really should be doing, which is getting the first item from the enumerable list and calling TypeDescriptor.GetProperties (the object-based one), which will call your implementation of ICustomTypeDescriptor and allow you to substitute a property list of your choosing.
Now, there are certainly plenty of workarounds for this, and in fact, I wouldn't have run across it if it weren't for the fact that someone else wrote this method to return an array instead of (e.g., an generic List). But that's not the point; the point is that it shouldn't be doing this. If there are objects in the enumerable list, it should interrogate them, regardless of whether or not it is an array.
In any case, whether or not you agree with my classification of this as a glaring bug, I hope that maybe someone will benefit from the time it took to research this gotcha (because it most definitely is that). The short answer, though not the ideal, is to just not bind to arrays and use either an ArrayList or generic list or collection or whatever. And, by the way, if you're wondering how I figured all this out, there's a nifty little tool called .NET Reflector that provides many insights. It's not as easy as debugging (because you can't actually step into the code), but if you're good at reading and following control flow, it can help you gain a much deeper insight into OPC (other people's code) when the docs just aren't sufficient.
The opinions expressed herein are solely my own personal opinions, founded or unfounded, rational or not, and you can quote me on that.
Thanks to the good folks at dasBlog!
Copyright © 2017 J. Ambrose Little