dcsimg
LinkedIn
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Object Binding Tips and Tricks : Page 3

Visual Studio 2005 and the .NET Framework 2.0 greatly enhance the data binding story in Windows Forms. This article explores the classes, interfaces, and coding techniques you can use today in your Windows Forms applications.


advertisement
Data Management
As soon as you allow a user to update any values on any forms, you will need to perform validation on those values. When all of the values are valid you will need to provide the option to save the changes. Object binding has some features that aid in the validation and data management processes.

To bind to a set of radio buttons, you need to create separate business object properties for each radio button.
The first step in implementing validation using object binding is to add the ErrorProvider control to the form and set its binding source to the CustomerBindingSource created earlier in this article. By setting its binding source to the object binding source, the ErrorProvider can recognize errors from the business object.

Once you have the ErrorProvider in place, implement any field-level validation rules directly in the properties of the business object. Throw an exception in the property if the property is invalid. The text of the exception will then automatically appear in the ErrorProvider icon if the user-entered value is invalid.

As a simple example, suppose you have a business rule for the Customer business object that requires the user to enter the last name. You could implement this as follows.

In VB:

   Public Property LastName() As String
      Get
         Return _LastName
      End Get
      Set(ByVal value As String)
         If String.IsNullOrEmpty(value) Then
            Throw New Exception( _
               "The Last Name cannot be empty")
         End If
         _LastName = value
      End Set
   End Property
In C#:

   public string LastName
   {
      get { return _LastName;}
      set
      { 
         if (String.IsNullOrEmpty(value))
         {
            throw new Exception(
               "The Last Name cannot be empty");
         }
         _LastName = value;
      }
   }
Author's Note: Any unexpected exception generated by any code in any property procedure or code called by any property procedure will be caught by the object binding and displayed in the ErrorProvider. So if you have, for example, a casting error at runtime, you will not see this error. Instead, the object binding code will catch the error and display it in the ErrorProvider icon.

You can add any required validation directly in any of the business object properties and throw exceptions as needed. However, for a more flexible and reusable approach for validation, you could create a separate Validation class that performs several types of validation such as required field checking, string length checking, numeric checking, date range checking, and so on. Your Validation class could then maintain a collection of the validation errors instead of throwing exceptions. The business object should then implement IDataErrorInfo and expose that collection to the ErrorProvider. The details for this are beyond the scope of this article, but if readers express enough interest in this technique, I'll cover this topic in a future article.

Once you've validated the user-entered values, the business object needs to track what values have changed. This allows you to implement features such as asking the user to save changes on exit only if something is changed. It also provides a way to keep track of which objects have been updated, created or deleted so that they can be appropriately processed back to the original data source.

This data management requires four basic steps:

  1. Define constants for the data states.
  2. Add a property to the business object to retain the state.
  3. Implement the INotifyPropertyChanged interface (System.ComponentModel. INotifyPropertyChanged).
  4. Generate the PropertyChanged event.
You can implement these steps in every business object class, repeating much of the code. Alternatively, you can create a base business object class and implement the code for these steps in that base class. Then inherit from the base class for every business object class.

To keep the example simple, this code demonstrates implementation of these steps directly in the Customer business object class.

Step 1: Define the constants for the data states.

In VB:

   Public Enum EntityStateEnum
      Unchanged
      Added
      Deleted
      Modified
   End Enum
In C#:

   public enum EntityStateEnum
   {
      Unchanged,
      Added,
      Deleted,
      Modified,
   }
Step 2: Define a property to retain the state.

In VB:

   Private _EntityState As EntityStateEnum
   Public Property EntityState() As EntityStateEnum
      Get
         Return _EntityState
      End Get
      Private Set(ByVal value As EntityStateEnum)
         _EntityState = value
      End Set
   End Property
In C#:

   private EntityStateEnum _EntityState;
   public EntityStateEnum EntityState
   {
      get { return _EntityState; }
      private set { _EntityState = value; }
   }
Note that the implementation of the set accessor is private. This prevents other code from modifying the state.

Step 3: Implement the INotifyPropertyChanged interface. By implementing this interface and raising PropertyChanged events when a property value is changed, the object binding will automatically detect changes. This ensures that any changes made to the properties are updated in the user interface.

In VB:

   Public Class Customer
      Implements INotifyPropertyChanged
      Public Event PropertyChanged(ByVal sender As _
        Object, ByVal e As PropertyChangedEventArgs) _
        Implements INotifyPropertyChanged.PropertyChanged
In C#:

   class Customer : INotifyPropertyChanged
   public event PropertyChangedEventHandler 
      PropertyChanged;
Step 4, raise the PropertyChanged event.

In VB:

   Private Sub DataStateChanged(ByVal dataState As _
      EntityStateEnum, ByVal propertyName As String)
      ' Raise the event
      If Not String.IsNullOrEmpty(propertyName) Then
         RaiseEvent PropertyChanged(Me, _
            New PropertyChangedEventArgs(propertyName))
      End If
      ' If the state is deleted, mark it as deleted
      If dataState = EntityStateEnum.Deleted Then
         Me.EntityState = dataState
      End If
      If Me.EntityState = EntityStateEnum.Unchanged Then
         Me.EntityState = dataState
      End If
   End Sub
In C#:

   private void DataStateChanged(EntityStateEnum 
      dataState, string propertyName)
   {
      // Raise the event
      if (PropertyChanged != null && 
         propertyName != null)
      {
         PropertyChanged(this, new 
            PropertyChangedEventArgs(propertyName));
      }
      // If the state is deleted, mark it as deleted
      if (dataState == EntityStateEnum.Deleted)
      {
         this.EntityState = dataState;
      }
      if (this.EntityState == 
         EntityStateEnum.Unchanged)
      {
         this.EntityState = dataState;
      }
   }
This DataStateChanged method raises the PropertyChanged event and sets the appropriate data state. You call the DataStateChanged method in the property procedures of each property in your business object as follows.

In VB:

   Public Property LastName() As String
      Get
         Return _LastName
      End Get
      Set (ByVal value as String)
         If String.IsNullOrEmpty(value) Then
            Throw New Exception( _
               "The Last Name cannot be empty")
         End If
         If value <> _LastName Then
            Me.DataStateChanged( _
               EntityStateEnum.Modified, _
               "LastName")
         End If
         _LastName = value
      End Set
   End Property
In C#:

   public string LastName
   {
      get { return _LastName;}
      set
      { 
         if (String.IsNullOrEmpty(value))
         {
            throw new Exception(
               "The Last Name cannot be empty");
         }
         if (value != _LastName)
         {
            this.DataStateChanged(
               EntityStateEnum.Modified, 
               "LastName");
         }
         _LastName = value;
      }
   }
With the basic data management code in place, you can then implement additional features, like a simple read-only IsDirty property.

In VB:

   Public ReadOnly Property IsDirty() As Boolean
      Get
         Return Me.EntityState <> _
            EntityStateEnum.Unchanged
      End Get
   End Property
In C#:

   public Boolean IsDirty
   {
      get{ return 
         this.EntityState!=EntityStateEnum.Unchanged;}
   }
This property can be called by the user interface in the Closing event to determine whether or not to ask the user to save changes.

As mentioned earlier, you could add all of this data management code, including the IsDirty property, to a base class that every business object inherits from. That provides a basic set of functionally in every business object with one set of code.



Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date