Advanced Basics

Data Binding in Visual Basic .NET

Ken Spencer

Code download available at:AdvancedBasics0308.exe(223 KB)

Q How can I get the most out of data binding in the applications I write in Visual Basic® .NET?

Q How can I get the most out of data binding in the applications I write in Visual Basic® .NET?

A As I mentioned in last month's installment of Advanced Basics, data binding is alive and well in the Microsoft® .NET Framework and it's a great technology that makes dealing with data more useful in many types of applications. Let's pick up where I left off and look at some practical applications of data binding.

A As I mentioned in last month's installment of Advanced Basics, data binding is alive and well in the Microsoft® .NET Framework and it's a great technology that makes dealing with data more useful in many types of applications. Let's pick up where I left off and look at some practical applications of data binding.

First, let's discuss architecture for a moment. Figure 1 shows an overview of a common way of using data binding with a data source. The data is pulled from the source by way of a data library and placed in an untyped DataSet. The untyped DataSet is used in order to enable the use of a generic data access library. This lets you call a single function that returns any DataSet. You can now take this untyped DataSet and merge it with a typed DataSet by using two matching tables. Finally, you can bind controls to the typed DataSet.

Figure 1 Binding

Figure 1** Binding **

You might wonder why I didn't bind directly to the untyped DataSet. Well, the typed DataSet provides features such as early binding of fields. This enables you to use .CustomerName as a member of the DataSet instead of referencing the Items collection with the value "CustomerName," making the process of accessing data much more straightforward. This early binding also gives you automatic support for data binding at design time. This enables you to select fields in the editor and even access custom properties in certain controls.

Now let's look at data binding in the real world. Take a look at the following function from my July 2003 column that pulls data from SQL Server™ and returns a DataSet:

Function RetrieveCustomerContacts() As DataSet Dim ds As DataSet Try ds = RunSQLWithDataSet("Select CustomerID, " & _ "CompanyName, ContactName, NoOfCustomerVisits " & _ "from customers", ConnectionString, "Customers") Catch ex As Exception ds = Nothing End Try Return ds End Function

Next, consider a typed DataSet named Customers with the following fields that I've added to the project as dsCustomers.xsd (part of the XSD has been omitted to conserve space):

<xs:sequence> <xs:element name="CustomerID" type="xs:string" /> <xs:element name="CompanyName" type="xs:string" /> <xs:element name="ContactName" type="xs:string" minOccurs="0" /> <xs:element name="NoOfCustomerVisits" type="xs:int" minOccurs="0" /> </xs:sequence>

In Visual Studio® .NET, you can wire up this data to a DataSet by dragging a DataSet component from the toolbox and dropping it onto your form, then associating it with the typed DataSet named Customers. Now you can use the properties window of a DataGrid control to select the new DataSet as the DataSource and the Customers table as the DataMember.

You can complete the process by adding the following code in the form's Load event:

Dim ds As DataSet Dim oClass As New SomeComponent.Class1 Try ds = oClass.RetrieveCustomerContacts dsCustomers.Merge(ds) Catch ex As Exception sbarmain.Text = ex.Message End Try

This code calls the RetrieveCustomerContacts class to retrieve the DataSet and merge it into the typed DataSet for your form. Of course, you can also perform this type of data binding with other controls such as the listbox and combobox controls. There is another cool feature that comes into play. The same code I just showed can be used to cause the typed DataSet to be loaded into your combobox. Then you can extract the chosen value with just a single line of code in the SelectedIndexChanged event:

txtSelectedID.Text = ComboBox1.SelectedValue

Of course, if you are binding the txtSelectedID control to a class or structure where the property is called CustomerID, you could do this instead of putting the data directly into the control:

CustomerContacts.CustomerID = ComboBox1.SelectedValue

This would update the class's CustomerID field and the data binding would update the value shown in the bound textbox.

Let's take a look at another way data binding makes your data-driven application more user-friendly. Say, for example, you have an application that has to show data in three textboxes from a DataSet or other bindable object. The CurrencyManager can be used to navigate through these controls and provide other features. I looked at several examples using the CurrencyManager and they were fairly obtuse, so I borrowed a concept from a sample in the MSDN® Library and made a more practical example. The interface to this form is shown in Figure 2.

Figure 2 Currency Manager Interface

Figure 2** Currency Manager Interface **

This form allows you to scroll through a DataSet and make changes to the data as you go. Each time you change a value and move to another record, the change is persisted to the DataSet. That's pretty much how the data binding worked in Visual Basic 6.0 and before. It's more flexible now with Visual Basic .NET because you have access to the low-level features of data binding.

Let's take a look at the code in Figure 3 so I can share a couple of things I learned. The code is pretty straightforward, but requires a bit of explanation. I will skip the mundane parts such as the class that pulls the data from SQL Server and returns it, but the complete code is in the download for this column.

Figure 3 Key Code Elements For Data Binding

Private WithEvents thisCurrencyManager As CurrencyManager 'Variable defs go here 'Form Load builds the dataset, merges it with CustomerInfo1, 'then binds the controls by calling BindControls Private Sub BindControls(ByVal thisDataTable As DataTable) ' Bind a TextBox control to a DataTable column 'in a DataSet. txtCompany.DataBindings.Add("Text", thisDataTable, "CompanyName") txtName.DataBindings.Add("Text", thisDataTable, "ContactName") txtCity.DataBindings.Add("Text", thisDataTable, "City") txtCustomerID.DataBindings.Add("Text", thisDataTable, "CustomerID") ' Specify the CurrencyManager for the DataTable. thisCurrencyManager = _ CType(Me.BindingContext(thisDataTable), CurrencyManager) ' Set the initial Position of the control. thisCurrencyManager.Position = 0 End Sub Private Sub MoveNext(ByVal thisCurrencyManager As CurrencyManager) If thisCurrencyManager.Position = thisCurrencyManager.Count - 1 Then MessageBox.Show("You're at end of the records") Else thisCurrencyManager.Position += 1 CheckChanges() End If End Sub Private Sub MoveFirst(ByVal thisCurrencyManager As CurrencyManager) thisCurrencyManager.Position = 0 CheckChanges() End Sub Private Sub MovePrevious(ByVal thisCurrencyManager As CurrencyManager) If thisCurrencyManager.Position = 0 Then MessageBox.Show( _ "You're at the beginning of the records.") Else thisCurrencyManager.Position -= 1 CheckChanges() End If End Sub Private Sub MoveLast(ByVal thisCurrencyManager As CurrencyManager) thisCurrencyManager.Position = thisCurrencyManager.Count - 1 CheckChanges() End Sub 'Button click events go here Private Sub txtName_TextChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles txtName.TextChanged 'Exit if starting up If IsNothing(thisCurrencyManager) Then Exit Sub End If StartEditMode() End Sub Private Sub txtCompany_TextChanged(ByVal sender As _ System.Object,ByVal e As System.EventArgs) _ Handles txtCompany.TextChanged StartEditMode() End Sub Private Sub txtCity_TextChanged(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles txtCity.TextChanged StartEditMode() End Sub Sub StartEditMode() cmdSaveChanges.Enabled = True End Sub Sub EndEditMode() Me.BindingContext(CustomerInfo1.Customers).EndCurrentEdit() End Sub Private Sub thisCurrencyManager_PositionChanged( _ ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles thisCurrencyManager.PositionChanged cmdSaveChanges.Enabled = False End Sub Private Sub cmdNew_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdNew.Click thisCurrencyManager.AddNew() End Sub 'Other subs go here

The first line of code in Figure 3 defines the CurrencyManager for my application. It's defined with WithEvents to allow the use of its events if I need them:

Private WithEvents thisCurrencyManager As CurrencyManager

The code in the frmBinder2_Load event is pretty standard. It calls a method that returns a DataSet, then merges that DataSet into a typed DataSet. The typed DataSet's Customers table is passed to the BindControls method, which performs the binding.

The BindControls method is where you see the CurrencyManager in action. The first few lines bind four fields from the DataSet to the Text property of the controls:

txtCompany.DataBindings.Add("Text", thisDataTable, "CompanyName") txtName.DataBindings.Add("Text", thisDataTable, "ContactName") txtCity.DataBindings.Add("Text", thisDataTable, "City") txtCustomerID.DataBindings.Add("Text", thisDataTable, "CustomerID")

The last two lines of BindControls actually use the CurrencyManager. The first of these lines sets the CurrencyManager to the BindingContext of the data source (a DataTable in this case):

thisCurrencyManager = CType(Me.BindingContext(thisDataTable), _ CurrencyManager)

The next line sets the initial position to 0 (the first record):

thisCurrencyManager.Position = 0

Now, I can proceed to the CurrencyManager. The Move methods all change the position within the DataSet by using the CurrencyManager. For instance, the MoveNext method moves the current element pointer forward in the data source as long as the current position is not the last element:

If thisCurrencyManager.Position = _ thisCurrencyManager.Count - 1 Then MessageBox.Show("You're at end of the records") Else thisCurrencyManager.Position += 1 CheckChanges() End If

The CheckChanges method is called each time the position changes to determine if the DataSet has any changes.

As I built this sample application, I struggled with one small problem for hours. I could scroll through the records fine and changing the data worked as long as I changed to another row in the data. However, when I used the Save button to save the changes, the GetChanges method was not properly indicating that changes had occurred. I checked the code per the MSDN documentation and the online samples and after a bit of sleuthing, I figured it out.

I was using a DataTable as the data source. The EndCurrentEdit method was being fired by the EndEditMode procedure with the following line of code:

Me.BindingContext(CustomerInfo1, "Customers").EndCurrentEdit()

This did not work; the DataSet showed no changes even though the data did, in fact, change. Finally, I realized I was using the wrong context. The following context worked just fine:

Me.BindingContext(CustomerInfo1.Customers).EndCurrentEdit()

In this case, I am passing only the DataTable which is indeed the data source for the controls. The funny thing is, the original code didn't generate any errors when it executed; it just didn't work. I'm sure the line did actually execute because when I changed the table name (Customers) to CustomersX, the line generated a runtime error. As I said, the code was executing, it was just pointing to the wrong data source.

The other code in this form is pretty straightforward as it implements calls to the move methods and otherwise handles interactions with the CurrencyManager.

The cmdAddNew_Click event is noteworthy because it causes a new row to be added to the data. This clears the form and allows you to enter data into the new row. You can see the changes are automatically saved by moving off the row and back.

The thisCurrencyManager_PositionChanged event is fired when the position is changed. This event is used to reset the state of the SaveChanges button.

For further information on data binding in MSDN Magazine, see Data Binding.

Send your questions and comments for Ken to  basics@microsoft.com.

Ken Spencer works for 32X Tech (https://www.32X.com), where he provides training, software development, and consulting services on Microsoft technologies.