Browse DevX
Sign up for e-mail newsletters from DevX


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.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

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.

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