Behind the Scenes: Improvements to Windows Forms Data Binding in the .NET Framework 2.0, Part 1

 

Cheryl Simmons
Microsoft Corporation

August 2006

Applies to:
   Microsoft Visual Studio 2005
   Microsoft .NET Framework 2.0
   Microsoft Windows Forms

Summary: Learn about additions to the Binding and ControlBindingsCollection classes that enable easier formatting of bound data. (9 printed pages)

Download code samples in C# and Visual Basic (23 KB) at the Microsoft Download Center.

Contents

Introduction
Formatting and Advanced Binding Dialog Box
Automatic Formatting Using Code
Using DataSourceUpdateMode
Automatic Handling of Null and DBNull Values
Improved Handling of Errors and Exceptions
Conclusion

Introduction

The most important addition to Windows Forms data binding is probably the BindingSource component. The BindingSource component simplifies the developer's job by providing currency management, change notification and the ability to easily access the members in a bound list. There are, however, some other lesser-known improvements to the data binding story worth discussing, and in fact, are important additions that complement the functionality offered by the BindingSource component.

The Binding object has several new members in the .NET Framework 2.0 that enable greater control over the binding operation. For example, you can control how data is formatted in a bound control, when the data source is updated, and how null and DBNull values in the data source are handled. These new members are also supported with corresponding Add methods in the ControlBindingsCollection. You can take advantage of these additions by using the Formatting and Advanced Binding dialog box in Visual Studio or through code. In addition, the Binding object has better support for handling exceptions and errors that can occur in the binding process with the addition of the BindingComplete event. This article describes these changes to the Binding and ControlBindingsCollection objects that you can take advantage of in your application whether or not you choose to use a BindingSource component.

This article starts by introducing the Formatting and Advanced Binding dialog box. The rest of this article focuses on how to complete the same types of tasks you would with the dialog box, but in code. Three of the examples use a DataTable, and the fourth uses a BindingSource object. All the examples can be used with or without a BindingSource as the data source for the application, although using a BindingSource, especially if you are binding multiple controls to the same data source, is recommended.

Formatting and Advanced Binding Dialog Box

The Formatting and Advanced Binding dialog box in Visual Studio lets you bind control properties to a data source and format the result. This dialog box is designed to take advantage of the changes to the Binding object. To access the Formatting and Advanced Binding dialog box in Visual Studio, right-click the control you want to bind to data, and select Properties. In the Properties window shown in Figure 1, expand the (DataBindings) item and select the (Advanced) item.

Figure 1. DataBindings settings in the Properties window

Click the ellipsis button next to (Advanced) to open the Formatting and Advanced Binding dialog box, shown in Figure 2.

Figure 2. The Formatting and Advanced Binding dialog box

When using the Formatting and Advanced Binding dialog box, you select a data source for your application. Your data source can be a BindingSource object, a database table, a Web Service type, or an object. Once you have selected a data source, if the data source is not a BindingSource, a BindingSource for the data source is automatically created. You can then set how the data source is updated when a change is made to the control value, as well as the format type and null value for the binding.

Note   It is important to note that most of the tasks described in this article can be accomplished using the Formatting and Advanced Binding dialog box.

Automatic Formatting Using Code

In earlier versions of the .NET Framework you had to manually perform the type conversions and formatting using the Format and Parse events of the Binding object. You can now do this by enabling formatting on the Binding object, either by setting the FormattingEnabled property directly or passing true to the Add method of the ControlBindingsCollection. In addition, you will need to specify a format string, either using the Add method, or by setting the FormatString property of the Binding object.

The following code includes two formatting examples. The first formatting example shows how to create a binding between a DateTime value in the data source and the Text value of a TextBox control. The FormattingEnabled property is set on the Binding object, the FormatString value specifies that the DateTime should be formatted as a long date/time string, and the binding is added to the collection.

The second formatting example demonstrates how to create a Binding object that binds data in a DataTable to the Text property of TextBox controls and format the data. The hire date is formatted as a long date and the starting salary is formatted as currency. This example uses the Add method of the ControlBindingsCollection to create the Binding object. This example also sets the FormatString of the binding directly. You will see examples of how to set the FormatString using the Add method of the ControlBindingsCollection later in this article.

Dim table1 As New DataTable()
table1.Columns.Add("Name", GetType(String))
table1.Columns.Add("Hire Date", GetType(DateTime))
table1.Columns.Add("Starting salary", GetType(Integer))

table1.Rows.Add(New Object() {"Claus Hansen", New DateTime(2003, 5, 5), _
  63000})
Dim dateBinding As New Binding("Text", table1, "Hire Date")

' Set FormattingEnabled and FormatString directly on the Binding object.
' The "D" format specifier indicates the long date pattern.
dateBinding.FormattingEnabled = True
dateBinding.FormatString = "D"

' Add the binding to the dateTextBox binding collection.
dateTextBox.DataBindings.Add(dateBinding)

' Set FormattingEnabled as part of the 
' ControlBindingsCollection.Add method.
Dim salaryBinding As Binding = salaryTextBox.DataBindings.Add("text", _
  table1, "Starting Salary", True)

' The "c" format specifier indicates currency format.
salaryBinding.FormatString = "c"

Dim nameBinding As Binding = nameTextBox.DataBindings.Add("Text", table1, _
  "Name", True)

The results are shown in Figure 3.

Figure 3. Formatted Currency and Date

Using DataSourceUpdateMode

The DataSourceUpdateMode enumeration lets you specify when control property changes are propagated to the data source. The default value DataSourceUpdateMode.OnValidation means the data source is updated when the user tabs away from a control with changes to a data bound value. DataSourceUpdateNever indicates changes to a bound control value are never propagated to the data source, and OnPropertyChange indicates the changed value will be propagated when any change to the control value is made. With OnPropertyChange or OnValidation, if the control value does not pass validation, the user will not be able to tab away from the control or close the form until a correct value is entered.

The following example demonstrates how to set the DataSourceUpdateMode using the Add method of the ControlBindingsCollection. The hire date in the DataTable is updated when any change to the hire date text box is made. The starting salary in the DataTable is updated when the starting salary text box loses the focus and the value passes validation. If an invalid value (such as one or more letters) is entered in to the starting salary text box, the user can't tab away from the text box.

Dim table1 As New DataTable()
table1.Columns.Add("Name", GetType(String))
table1.Columns.Add("Hire Date", GetType(DateTime))
table1.Columns.Add("Starting salary", GetType(Integer))
table1.Rows.Add(New Object() {"Claus Hansen", New DateTime(2003, 5, 5), _
  63000})

' Hire Date in table1 is updated when any change to the hire date
' text box is made.
Dim dateBinding2 As Binding = dateTextBox.DataBindings.Add("Text", table1, _
  "Hire Date", True, DataSourceUpdateMode.OnPropertyChanged)
dateBinding2.FormatString = "D"

' Starting Salary in table1 is updated when the starting salary
' text box loses focus and the value passes validation.
Dim salaryBinding As Binding = salaryTextBox.DataBindings.Add("text", _
  table1, "Starting salary", True, DataSourceUpdateMode.OnValidation)
salaryBinding.FormatString = "c"

Dim nameBinding As Binding = nameTextBox.DataBindings.Add("Text", table1, _
  "Name", True)

Automatic Handling of Null and DBNull Values

In earlier versions of Windows Forms data binding, when handling the Format and Parse events, you had to consider how you would want a DBNull or null value in the data source to appear in the control. This would require that you parse a particular control value to null or DBNull in the data source. This is now much easier with the NullValue property of Binding object.

The following example demonstrates how a DBNull value in the data source is displayed with the NullValue property in the bound control. The hire date is a DBNull value in the DataTable, but is displayed as "New Hire" in the text box.

Dim table1 As New DataTable()
table1.Columns.Add("Name", GetType(String))
table1.Columns.Add("Hire Date", GetType(DateTime))
table1.Columns.Add("Starting salary", GetType(Integer))

' Initialize Hire Date to DBNull.
table1.Rows.Add(New Object() {"Claus Hansen", DBNull.Value, 63000})

' If Hire Date is null or DBNull, change to "New Hire".
Dim dateBinding2 As Binding = dateTextBox.DataBindings.Add("Text", table1, _
  "Hire Date", True, DataSourceUpdateMode.OnValidation, "New Hire", "D")

Dim salaryBinding As Binding = salaryTextBox.DataBindings.Add("text", _
  table1, "Starting salary", True)
salaryBinding.FormatString = "c"

Dim nameBinding As Binding = nameTextBox.DataBindings.Add("Text", table1, _
  "Name", True)

The results are shown in Figure 4.

Figure 4. DBNull formatted as the string "New Hire"

Improved Handling of Errors and Exceptions

Whenever you enable formatting on a data binding you now have greater control over error and exception handling using the BindingComplete event of the Binding object.

The following example shows how you might handle the BindingComplete event to provide feedback to the user when a bound control value is changed to an invalid value. This example shows a business object, Employee, which does not allow for a value of less than 20000 for its Salary property. For binding purposes, the employee objects are stored in a BindingSource.

First, I create the BindingSource, add two Employee objects, and establish the bindings. I also associate the BindingComplete event with its event-handling method.

' Declare the Binding object.
Dim WithEvents salaryBinding As Binding

Dim employeeBindingSource As New BindingSource()
employeeBindingSource.Add(New Employee("Hansen", "Claus", 63000, _
  New DateTime(2002, 5, 5)))
employeeBindingSource.Add(New Employee("Han", "Mu", 54000, _
  New DateTime(2001, 3, 4)))
nameTextBox.DataBindings.Add("Text", employeeBindingSource, _
  "LastName", True)
dateTextBox.DataBindings.Add("Text", employeeBindingSource, _
  "StartDate", True, DataSourceUpdateMode.OnValidation)

salaryBinding = salaryTextBox.DataBindings.Add("text", _
  employeeBindingSource, "Salary", True, DataSourceUpdateMode.OnValidation, _
  "Unknown", "c")

Next, I handle the BindingComplete event. This event-handler passes the error message to an ErrorProvider control.

Sub salaryBinding_BindingComplete(ByVal sender As Object, _
  ByVal e As BindingCompleteEventArgs) Handles salaryBinding.BindingComplete

    ' If the BindingComplete state is anything other than success, 
    ' set the ErrorProvider to the error message.
    If e.BindingCompleteState <> BindingCompleteState.Success Then
        errorProvider1.SetError( _
          CType(e.Binding.BindableComponent, Control), e.ErrorText)
    Else
        errorProvider1.SetError( _
          CType(e.Binding.BindableComponent, Control), "")
    End If
End Sub

The following code shows the Employee business object.

Public Class Employee
    Private lastNameValue As String
    Private firstNameValue As String
    Private salaryValue As Integer
    Private startDateValue As DateTime
    
    Public Sub New(ByVal lastName As String, ByVal firstName As String, _
      ByVal salary As Integer, ByVal startDate As DateTime)
        lastNameValue = lastName
        firstNameValue = firstName
        salaryValue = salary
        startDateValue = startDate
    End Sub

    Public Property LastName() As String
        Get
            Return lastNameValue
        End Get
        Set(ByVal value As String)
            lastNameValue = value
        End Set
    End Property
    
    Public Property FirstName() As String 
        Get
            Return firstNameValue
        End Get
        Set
            firstNameValue = value
        End Set
    End Property 
    
    Public Property Salary() As Integer 
        Get
            Return salaryValue
        End Get
        Set
            If value < 20000 Then
                Throw New Exception("Salary cannot be less than $20,000")
            Else
                salaryValue = value
            End If
        End Set
    End Property 
    
    Public Property StartDate() As DateTime 
        Get
            Return startDateValue
        End Get
        Set
            startDateValue = value
        End Set
    End Property
    
    Public Overrides Function ToString() As String 
        Return LastName & ", " & FirstName & vbLf & "Salary:" _
          & salaryValue & vbLf & "Start date:" & startDateValue
    End Function
End Class

In order to see the error-handling occur, run the example and change the salary to a value less than $20,000. Figure 5 is a screen shot of the feedback to the user by handling the BindingComplete event.

Figure 5. Example that shows how the BindingComplete event can be used to verify the data in a bound control

Conclusion

The BindingSource might be one of the most talked about additions for Windows Forms data binding, but there are many other useful features that were added to the Binding and ControlBindingsCollection objects that complement the functionality of the BindingSource. With these changes you can easily format bound data, handle null values in the data source and gracefully handle exceptions that occur in the binding process, whether or not you use a BindingSource component on your form. For more information on Windows Forms, see:

Continue to Behind the Scenes: Improvements to Windows Forms Data Binding in the .NET Framework 2.0, Part 2.