Wednesday, January 31, 2007

Today I have encountered a strange problem with GridView's AutoGenerateColumns property. The problem was that not all columns were generated automatically. It would not be a surprise if it happened for complex types such as my own, but I have noticed that simple (in my opinion) types such as Nullable<Guid> were also not generated! So I went investigating.

After a while of hacking and slashing through the GridView's code I have found the reason. It appears that columns are only auto-generated for properties whose type is a bindable type. What does that mean? Who decides if a type is bindable or not? I have continued my investigation and...

After a while I have found that there is a method on a GridView control called IsBindableType which looks like this:

public virtual bool IsBindableType(Type type)
      if ((!type.IsPrimitive && (type != typeof(string))) && 
      ((type != typeof(DateTime)) && (type != typeof(decimal)))) { return (type == typeof(Guid)); } return true; }

Apparently this method decides that Nullable<Guid> (or Nullable<int> for that matter) is not a bindable type! Because of this, you will not see properties of such a type automatically rendered in a GridView!

Being curious I have asked myself: why such a method is defined on a GridView control? Is it not a more general issue to decide if a type is bindable or not? So I went investigating even further.

I have found that there are 5 IsBindableType methods defined in System.Web.UI.WebControls namespace:

  • AdRotator
  • BaseDataList
  • DetailsView
  • FormView
  • GridView

Of those 5, DetailsView, FormView and GridView have exactly the same implementation. BaseDataList has slightly different:

public static bool IsBindableType(Type type)
      if ((!type.IsPrimitive && (type != typeof(string))) 
      && (type != typeof(DateTime))) { return (type == typeof(decimal)); } return true; }

Notice the static modifier! AdRotator on the other hand has exactly the same implementation as the BaseDataList but here the method is an instance method and is private!

So, we have basically 5 places in code, each of which implements the same logic, some do it differently and none does it correctly :-(.

kick it on