Monthly Archives: February 2007

Presenting the SortableBindingList<T>

If you are databinding your custom objects (in a Bindinglist of <T>) to a DataGridView you will notice that the users can’t sort the rows by clicking on the columnheaders… Unlike an unbound DataGridView, the SortCompare event is not raised. Here is a class that uses IComparer to implement a BindingList that supports Sorting:

Please read the follow up article to find the updated source code.

Using this SortableBindingList is as easy as:

public Form1()
{
 InitializeComponent();

 SortableBindingList<person> persons = new SortableBindingList<person>();
 persons.Add(new Person(1, "timvw", new DateTime(1980, 04, 30)));
 persons.Add(new Person(2, "John Doe", DateTime.Now));

 this.dataGridView1.AutoGenerateColumns = false;
 this.ColumnId.DataPropertyName = "Id";
 this.ColumnName.DataPropertyName = "Name";
 this.ColumnBirthday.DataPropertyName = "Birthday";
 this.dataGridView1.DataSource = persons;
}



the sortablebindinglist at work

Feel free to download the source and demoproject: SortableBindingList.zip.

Edit: You can find the latest implementation at BeTimvwFramework, a project where i will keep classes that i find interesting.

Don't wait until the DateTimePicker has lost focus to write back the values

Drag a TextBox and a DateTimePicker control on a Form and databind them to a DateTime property, eg:

public partial class Form1 : Form
{
 public Form1()
 {
  InitializeComponent();

  SimpleObject simpleObject = new SimpleObject();
  simpleObject.Birthday = DateTime.Now;

  this.dateTimePicker1.DataBindings.Add("Value", simpleObject, "Birthday");
  this.textBox1.DataBindings.Add("Text", simpleObject, "Birthday", true, DataSourceUpdateMode.OnPropertyChanged);
 }
}

public class SimpleObject : INotifyPropertyChanged
{
 private DateTime birthday;

 public event PropertyChangedEventHandler PropertyChanged;

 public DateTime Birthday
 {
  get { return this.birthday; }
  set
  {
   this.birthday = value;
   this.OnPropertyChanged("Birthday");
  }
 }

 private void OnPropertyChanged(string propertyName)
 {
  if (this.PropertyChanged != null)
  {
   this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }
 }
}

The annoying bit is that every time the user picks a datetime, he has to move the focus before the changes in the DateTimePicker control are written back to the datasource… You can circumvent this by handling the CloseUp event of the DataTimePicker as following:

private void dateTimePicker1_CloseUp(object sender, EventArgs e)
{
 DateTimePicker dateTimePicker = sender as DateTimePicker;
 if (dateTimePicker != null)
 {
  foreach (Binding binding in dateTimePicker.DataBindings)
  {
   binding.WriteValue();
  }
 }
}

I think mosts users will appreciate this new behaviour :) We can also apply this technique on a ComboBox (using the SelectionChangeCommitted event). Instead of manually hooking up to all these events, i’ve implemented an IExtenderProvider that takes care of this tedious task (only showing the part for the datetimepicker)

[ProvideProperty("WriteValuesAfterCloseUp", typeof(DateTimePicker))]
class WriteValueAfterEventExtender : Component, IExtenderProvider
{
 private Dictionary<dateTimePicker, bool> writeValuesAfterCloseUp;

 public WriteValueAfterEventExtender()
 {
  this.writeValuesAfterCloseUp = new Dictionary<dateTimePicker, bool>();
 }

 public bool CanExtend(object extendee)
 {
  return extendee is DateTimePicker;
 }

 #region WriteValuesAfterCloseUp

 [Description("Gets a boolean indicating if the values are written to the datasource after a CloseUp event.")]
 public bool GetWriteValuesAfterCloseUp(DateTimePicker dateTimePicker)
 {
  bool value;
  if (!this.writeValuesAfterCloseUp.TryGetValue(dateTimePicker, out value))
  {
   value = false;
  }
  return value;
 }

 public void SetWriteValuesAfterCloseUp(DateTimePicker dateTimePicker, bool value)
 {
  if (this.writeValuesAfterCloseUp.ContainsKey(dateTimePicker))
  {
   this.writeValuesAfterCloseUp[dateTimePicker] = value;
  }
  else
  {
   this.writeValuesAfterCloseUp.Add(dateTimePicker, value);
  }

  if (value)
  {
   dateTimePicker.CloseUp += this.dateTimePicker_CloseUp;
  }
  else
  {
   dateTimePicker.CloseUp -= this.dateTimePicker_CloseUp;
  }
 }

 private void dateTimePicker_CloseUp(object sender, EventArgs e)
 {
  DateTimePicker dateTimePicker = sender as DateTimePicker;
  if (dateTimePicker != null)
  {
   foreach (Binding binding in dateTimePicker.DataBindings)
   {
    binding.WriteValue();
   }
  }
 }

 #endregion
}

As soon as you drop an instance of the WriteValueAfterEditExtender component on your designer form you will see the that an extra property appears on the datetimepicker:

image of the propertylist for datetimepicker

As always, feel free to download the ExtenderProvider.zip.

Control the order of Properties in your Class

Sometimes you want to manipulate the order in which properties are used for databinding. Eg: If you drag and drop an object datasource on a DataGridView you have no control in which order it binds the properties. Offcourse, you can order the columns by moving them around… Today someone asked the following:

I would like to have it come from the class in the order I want it. Any suggestions on how to set the display order without referencing the actual member names?

I started with the implementation of a PropertyOrderAttribute:

[AttributeUsage(AttributeTargets.Property)]
public class PropertyOrderAttribute : Attribute
{
 private int order;

 public PropertyOrderAttribute(int order)
 {
  this.order = order;
 }

 public int Order
 {
  get { return this.order; }
 }
}

So the user can use this attribute to define the order in which the properties should appear as following:

class Foo
{
 private int id;
 private string name;
 private DateTime birthDay;

 public Foo(int id, string name, DateTime birthDay)
 {
  this.id = id;
  this.name = name;
  this.birthDay = birthDay;
 }

 [PropertyOrder(0)]
 public int Id
 {
  get { return id; }
  set { id = value; }
 }

 [PropertyOrder(2)]
 public string Name
 {
  get { return name; }
  set { name = value; }
 }

 [PropertyOrder(1)]
 public DateTime BirthDay
 {
  get { return birthDay; }
  set { birthDay = value; }
 }
}

And now i implement a generic BindingList that makes use of the PropertyOrderAttributes:

class PropertyOrderBindingList<t> : BindingList<t>, ITypedList
{
 public PropertyOrderBindingList()
  : base()
 {
  //
 }

 public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
 {
  PropertyDescriptorCollection typePropertiesCollection = TypeDescriptor.GetProperties(typeof(T));
  return typePropertiesCollection.Sort(new PropertyDescriptorComparer());
 }

 public string GetListName(PropertyDescriptor[] listAccessors)
 {
  return string.Format("A list with Properties for {0}", typeof(T).Name);
 }
}

class PropertyDescriptorComparer : IComparer
{
 public int Compare(object x, object y)
 {
  if (x == y) return 0;
  if (x == null) return 1;
  if (y == null) return -1;

  PropertyDescriptor propertyDescriptorX = x as PropertyDescriptor;
  PropertyDescriptor propertyDescriptorY = y as PropertyDescriptor;

  PropertyOrderAttribute propertyOrderAttributeX = propertyDescriptorX.Attributes[typeof(PropertyOrderAttribute)] as PropertyOrderAttribute;
  PropertyOrderAttribute propertyOrderAttributeY = propertyDescriptorY.Attributes[typeof(PropertyOrderAttribute)] as PropertyOrderAttribute;

  if (propertyOrderAttributeX == propertyOrderAttributeY) return 0;
  if (propertyOrderAttributeX == null) return 1;
  if (propertyOrderAttributeY == null) return -1;

  return propertyOrderAttributeX.Order.CompareTo(propertyOrderAttributeY.Order);
 }
}

With all this infrastructure it becomes as easy as:

public Form1()
{
 InitializeComponent();

 PropertyOrderBindingList<foo> fooList = new PropertyOrderBindingList<foo>();
 fooList.Add(new Foo(1, "Timvw", new DateTime(1980, 4, 30)));
 fooList.Add(new Foo(2, "Mike", new DateTime(1984, 1, 1)));
 this.dataGridView1.DataSource = fooList;
}