ITypedList and PropertyDescriptors

A few days ago, Ayende mentioned a problem he ran into while using BindingList<T> and databinding against a grid when the list contained a mixture of objects from classes in a class hierarchy, and proposed a solution involving the use of a custom TypeDescriptionProvider class.

I suggested that perhaps implementing ITypedList on his collection might be a solution to his problem, while being somewhat more ellegant than a custom TypeDescriptionProvider. Here's a simple implementation of ITypedList on top of BindingList<T> that's close to some code I've been using lately:

public class TypedBindingList<T> : BindingList<T>, ITypedList

{

   #region ITypedList Implementation

   //

   // ITypedList Implementation

   //

 

   public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)

   {

      if ( listAccessors == null || listAccessors.Length <= 0 )

      {

         // return the properties of items on

         // this list

         return GetTypeProperties(typeof(T));

      } else

      {

         // return the properties of the specified member

         // this is needed when the list is used in master/detail

         // structures to ensure the child grid can figure out

         // the types contained herein.

         string memberName = listAccessors[0].Name;

         PropertyInfo pinfo = typeof(T).GetProperty(memberName);

         if ( pinfo != null )

         {

            Type type = pinfo.PropertyType;

            if ( typeof(IList).IsAssignableFrom(type) && type.IsGenericType )

            {

               // if it is a generic list, find the first generic type and

               // assume that's the type of the list contents

               // a hack, but it could be worse :)

               Type paramType = type.GetGenericArguments()[0];

               return GetTypeProperties(paramType);

            }

            return GetTypeProperties(type);

         }

         return null;

      }

   }

 

   public string GetListName(PropertyDescriptor[] listAccessors)

   {

      return string.Format("List of {0}", typeof(T).Name);

   }

 

   #endregion // ITypedList Implementation

 

   private PropertyDescriptorCollection GetTypeProperties(Type type)

   {

      TypeDescriptionProvider provider = TypeDescriptor.GetProvider(type);

      return provider.GetTypeDescriptor(type).GetProperties();

   }

 

} // class TypedBindingList

 

It's not pretty, but it does the trick without too much hassle. In particular, it has been very useful in binding empty collections to a third-party grid control so that it still allows you to add new records (the ITypedList implementation provides the information it needs to ensure it can handle the new record returned by IBindingList.AddNew()). Of course, you can get much fancier when implementing this stuff. Frans Bouma has some pretty good articles on these kind of topics including this one.

2 Comments

  1. Sam

    Sorry, but this doesn’t work real well when there is a BindingList in a BindingList in a BindingList:

    class Order
    {
    public int Qty;
    public double Price;
    public string Description;
    }

    class Customer
    {
    public string CustomerName { get; set; }
    public BindingList Orders { get; set; }
    }

    class Company
    {
    public string CompanyName { get; set; }
    public BindingList Employees { get; set; }
    }

    class CompanyCollection : TypedBindingList
    {
    public CompanyCollection()
    {
    this.AllowEdit = true;
    this.AllowNew = true;
    this.AllowRemove = true;
    }

    ///
    ///
    ///
    public CompanyCollection(IList coll)
    : base(coll)
    {
    this.AllowEdit = true;
    this.AllowNew = true;
    this.AllowRemove = true;
    }
    }

    static class CompanyCollectionFactory
    {
    static private string[] products =
    {
    “cats”,”dogs”,”tv”,”vcr”, “milk”,”bmw z8″,”nikon D3s”,”cake”,”paper”
    };

    static public TypedBindingList Create()
    {
    CompanyCollection coll = new CompanyCollection();

    coll.Add(new Company { CompanyName = “A”, Employees = CreateCustomerBindingList(new string[] { “Sam”, “Mike”, “Brian” }) });
    coll.Add(new Company { CompanyName = “B”, Employees = CreateCustomerBindingList(new string[] { “Chris”, “George”, “Larry” }) });
    coll.Add(new Company { CompanyName = “C”, Employees = CreateCustomerBindingList(new string[] { “Gwen”, “Sue”, “Linda” }) });
    coll.Add(new Company { CompanyName = “D”, Employees = CreateCustomerBindingList(new string[] { “Kate”, “Jane”, “Marry” }) });

    return coll;
    }

    private static BindingList CreateCustomerBindingList(string[] names)
    {
    //BindingList coll = new CustomerCollection();
    BindingList coll = new BindingList();

    foreach (string s in names)
    {
    // coll.Add(new Customer { CustomerName = s, Orders = CreateOrderCollection() });
    coll.Add(new Customer { CustomerName = s, Orders = CreateOrderBindingList() });
    }
    return coll;
    }

    private static BindingList CreateOrderBindingList()
    {
    BindingList coll = new BindingList();
    Random rnd1 = new Random();
    for (int i = 0; i < 2; i++)
    {
    coll.Add(new Order
    {
    Qty = rnd1.Next(10) + 1,
    Description = products[rnd1.Next(products.Length)],
    Price = Math.Round(rnd1.NextDouble() * 500, 2)
    });
    }

    return coll;
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>