Getting the Most Out of Windows Forms Data Binding
page 3 of 6
by Jon Kruger
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 28223/ 65

Interfaces

INotifyPropertyChanged

The INotifyPropertyChanged interface is used to notify listeners that a property on the object has changed. When a property’s value changes, you will fire the PropertyChanged event; I do this in the example below in the NotifyPropertyChanged() method.

Your data binding may seem to work fine without implementing this interface, but that is because the data in your object is probably only changing when the user types in a new value to a control. What happens when something in the business object changes for some another reason?

For example, in the code sample below when a property changes, we are going to update the value in the LastUpdate property.

Listing 1

public class Person: INotifyPropertyChanged
{
  private string _firstName;
  private string _lastName;
  private string _email;
  private DateTime _lastUpdate;
 
  public string FirstName
  {
    get
    {
      return _firstName;
    }
    set
    {
      if (_firstName == value)
        return ;
 
      _firstName = value;
      NotifyPropertyChanged("FirstName");
    }
  }
 
  public string LastName
  {
    get
    {
      return _lastName;
    }
    set
    {
      if (_lastName == value)
        return ;
 
      _lastName = value;
      NotifyPropertyChanged("LastName");
    }
  }
 
  public string Email
  {
    get
    {
      return _email;
    }
    set
    {
      if (_email == value)
        return ;
 
      _email = value;
      NotifyPropertyChanged("Email");
    }
  }
 
  public DateTime LastUpdate
  {
    get
    {
      return _lastUpdate;
    }
  }
 
  public event PropertyChangedEventHandler PropertyChanged;
 
  protected void NotifyPropertyChanged(string propertyName)
  {
    if (propertyName != "LastUpdate")
    {
      _lastUpdate = DateTime.Now;
      NotifyPropertyChanged("LastUpdate");
    }
 
    if (PropertyChanged != null)
      PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
  }
}

Because we implemented INotifyPropertyChanged, a grid that is bound to this object will get the updated value of LastUpdate when one of the other properties changes.

IDataErrorInfo           

The IDataErrorInfo interface provides a way for your business object to provide error information to listeners, such as a grid or an ErrorProvider component.

Figure 1

The IDataErrorInfo interface has two properties — an Error property that returns an error message for the entire object and an indexer that returns an error message for a specific property.

I am not exactly sure why Microsoft chose to use an indexer in this interface. When you see code that says, “MessageBox.Show(person[”Email”]),” you would not naturally think that you would be displaying an error message for the Email property. In my opinion, something like GetError("Email") would have been more fitting.

But all opinions aside, your best choice is probably to implement this interface explicitly. By doing this, you will have to cast the object to an IDataErrorInfo before you can access the indexer.

Listing 2

Person person = new Person();
// If you implement IDataErrorInfo explicitly, this will not compile
MessageBox.Show(person["Email"]);
// If you implement IDataErrorInfo explicitly, you have to cast
// the Person object to IDataErrorInfo in order to access the
// indexer.  This allows you to have an indexer on your object
// for some other reason.
MessageBox.Show(((IDataErrorInfo) person)["Email"]);

IBindingList

When you bind a control such as a grid to a list, you should bind to a collection that implements IBindingList. The IBindingList interface contains methods and properties that deal with sorting of the list and the adding and deleting of items from the list. You can use the System.ComponentModel.BindingList class in most cases, so you do not necessarily have to know about all of the nitty gritty details of implementing IBindingList. The important thing to know is that a grid that is bound to an IBindingList will automatically update when items are added or removed from the underlying list.

When you want to add a new row to a data bound grid, you will have to do so in the underlying list by calling IBindingList methods.

Listing 3

// This line will throw an exception because you cannot
// call this method when the grid is data bound -- you
// have to add the item to the underlying list.
//this.dataGridView.Rows.Add();
// This is the correct way.
Person newPerson = _list.AddNew();

If you want to remove an item from the list or the grid, you can do it either by removing the object from the list or by telling the grid to remove the row.

Listing 4

// Either one of the methods below will work for
// removing the row from the list and the grid.
 
if (/* remove from the underlying list */)
{
    // Remove the item from the underlying list.
    _list.Remove((Person)row.DataBoundItem);
}
else
{
    // Tell the grid to remove the row
    this.dataGridView.Rows.Remove(row);
}

IEditableObject

The IEditableObject interface provides functionality to commit or rollback changes to an object that is used as a data source. The primary application of this is when you are editing a record in a grid and you want to click a Cancel button and undo all of the edits that you made to that record.

Implementing IEditableObject is fairly straightforward. There are three methods: BeginEdit(), CancelEdit(), and EndEdit(). In BeginEdit(), flag the object as being in edit mode and store off all of the values in the object. In CancelEdit(), clear the edit mode flag and restore all of the object values that you stored in BeginEdit(). In EndEdit(), clear the edit mode flag, forget all of the values that you stored in BeginEdit() and, if necessary, commit the changes to the object to your data store.

Here is the tricky part. If your object is bound to a grid, for example, the grid will call BeginEdit() anytime you edit any part of the record. So in your BeginEdit() method, you will want to keep track of whether the object is already in edit mode so that you do not go storing off values every time a cell is edited.

Listing 5

public void BeginEdit()
{
  if (!_doingEdit)
  {
    _originalValues = new Dictionary < stringobject > ();
    foreach (PropertyInfo pi in GetType().GetProperties())
    {
      if (pi.CanWrite)
        _originalValues[pi.Name] = pi.GetValue(thisnull);
    }
    _doingEdit = true;
  }
}

If you are editing a record in a grid, for example, and you select a different row, the grid will call EndEdit() for you. You can also call EndEdit() and CancelEdit() yourself (for example, when a Save or Cancel button is clicked).

The BindingSource component — tying it all together

In .NET 2.0 the BindingSource component was introduced. The BindingSource acts as a layer of abstraction between your control and your actual data source. In the designer you can set the DataSource property of the BindingSource to an object type, which will provide access to type information in the designer so that you can set up almost all of your data binding in the designer.

Figure 2

Setting the DataSource property of the BindingSource

Now if you select your BindingSource component as your grid’s DataSource, all of the columns will be added to the grid and you will be able to work with the columns at design-time.

Another benefit of the BindingSource component is that it will allow you to choose from a list of your object’s properties when you want to bind individual controls.

Figure 3

 

Setting data bindings on a textbox

 

Not only that, but if your form contains an ErrorProvider component on your form, you can set the DataSource of the ErrorProvider to the BindingSource. And if you have implemented IDataErrorInfo on your business object, the ErrorProvider will automatically display error messages in your form!


View Entire Article

User Comments

No comments posted yet.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2019 ASPAlliance.com  |  Page Processed at 2019-10-19 1:21:41 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search