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:
- Windows Forms on Microsoft .NET Framework Developer Center
- WindowsForms.NET
- Windows Forms Documentation Updates Blog
Continue to Behind the Scenes: Improvements to Windows Forms Data Binding in the .NET Framework 2.0, Part 2.