Unlike simple data-bound controls, the complex data-bound controls can display a set of data such as a single column or a collection of columns. The controls such as DataGrid are even able to display data from multiple tables of a database. Whereas the simple data-bound controls usually use their Text properties for binding, complex data-bound controls use their DataSource and DataMember properties.
In the following sections, we discuss some of the common complex data-bound controls such as the ComboBox, ListBox, and DataGrid.
The Control class is the mother of all Windows controls. This class's basic functionality is required by visual Windows controls that are available from the Toolbox or through other wizards. The Control class handles user input through the keyboard, the mouse, and other pointing devices. It also defines the position and size of controls; however, it doesn't implement painting.
If you count the Control class members, you'll find that this class is one of the biggest classes available in the .NET Framework Library. In the following sections, you'll explore some of the data-binding functionality implemented by this class.
The Control class provides two important and useful properties, which play a vital role in the data-binding process. These properties are BindingContext and DataBinding. The BindingContext property represents the BindingContext attached to a control. As discussed earlier, BindingContext returns a single BindingManagerBase object for all data-bound controls. The BindingManagerBase object provides the synchronization for all data-bound controls. The Control class also implements the DataSourceChanged event, which raises when the data source of a control is changed. We discuss this event in more detail shortly.
The ListControl class is the base class for ListBox and ComboBox controls and implements the data-binding functionality. The ListControl class provides four data-binding properties: DataManager, DataSource, DisplayMember, and ValueMember.
The DataManager (read-only) property returns the CurrencyManager object associated with a ListControl class.
The DataSource property (both get and set) represents the data source for a ListControl class.
The DisplayMember (both get and set) represents a string, which specifies the property of a data source whose contents you want to display. For example, if you want to display the data of a DataTable's Name column in a ListBox control, you set DisplayMember ="Name".
The ValueMember (both get and set) property represents a string, which specifies the property of the data source from which to draw the value. The default value of this property is an empty string ("").
You'll see how to use these properties in the following samples.
Besides the BindingContextChanged event, the ListControl class implements three data-binding events: OnDataSourceChanged, OnDisplayMemberChanged, and OnValueMemberChanged. The OnDataSourceChanged method raises the DataSourceChanged event. This event occurs when the DataSource property of a ListControl class is changed. The OnDisplayMemberChanged method raises the DisplayMemberChanged event, which occurs when the DisplayMember property of the control changes. The OnValueMemberChanged method raises the ValueMemberChanged event, which occurs when the ValueMember property of the control changes.
These events are useful when your program needs a notification when any of these events occur. Listing 7-11 attaches these events with event handlers. The code also shows the handler methods, which will be called when the event occurs.
Listing 7-11: Adding a ListBox Control Event Handler
' Bind data with controls Private Sub BindListControls() ComboBox1.DataSource = ds.Tables(0) ComboBox1.DisplayMember = "EmployeeID" ListBox1.DataSource = ds.Tables(0) ListBox1.DisplayMember = "FirstName" ListBox2.DataSource = ds.Tables(0) ListBox2.DisplayMember = "LastName" ListBox3.DataSource = ds.Tables(0) ListBox3.DisplayMember = "Title" End Sub Private Sub ComboDataSourceChangedMethod(ByVal sender As Object, _ ByVal cevent As EventArgs) Handles ListBox1.DataSourceChanged MessageBox.Show("Data Source changed") End Sub Private Sub DisplayMemberChangedMethod(ByVal sender As Object, _ ByVal cevent As EventArgs) Handles ListBox1.DisplayMemberChanged MessageBox.Show("Display Member changed") End Sub Private Sub ValueMemberChangedMethod(ByVal sender As Object, _ ByVal cevent As EventArgs) Handles ListBox1.ValueMemberChanged MessageBox.Show("Value Member changed") End Sub Private Sub BindingContextChangedMethod(ByVal sender As Object, _ ByVal cevent As EventArgs) Handles ListBox1.BindingContextChanged MessageBox.Show("Binding Context changed") End Sub
To raise these events, just change the value of the ListBox properties (see Listing 7-12).
Listing 7-12: Raising ListBox Events
Private Sub ListChangedEvents_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles ListChangedEvents.Click Dim custDataSet As DataSet = New DataSet() sql = "SELECT CustomerID, ContactName, City FROM Customers" custDataSet = New DataSet("Customers") conn = New SqlConnection() conn.ConnectionString = ConnectionString adapter = New SqlDataAdapter(sql, conn) adapter.Fill(custDataSet, "Customers") ListBox1.DataSource = custDataSet.Tables(0) ListBox1.DisplayMember = "ContactName" ListBox1.ValueMember = "ContactName" conn.Close() conn.Dispose() End Sub
Now you'll learn how to use complex data binding in a ComboBox and a ListBox control. Unlike simple data-bound controls, complex data-bound controls maintain the default binding synchronization. For instance, if you bind a data source with a ListBox and a ComboBox control, and then move from one item to another in a control, you can see the selection change in the second control respective to the item you select in the first control.
To prove this theory, you'll create a Windows application with a ComboBox and three ListBox controls. The final form looks like Figure 7-4.
Figure 7-4: ListBox and ComboBox data-binding form
As usual, you load data in the Form_Load event. Listing 7-13 shows the event handler code, where you call the FillDataSet and BindListControl methods.
Listing 7-13: The Form Load Event Handler
Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load FillDataSet() BindListControls() End Sub
The FillDataSet method simply opens the connection and fills data in a DataSet. Listing 7-14 shows this method.
Listing 7-14: The FillDataSet Method
' Fill DataSEt Private Sub FillDataSet() ds = New DataSet() sql = "SELECT * FROM Employees" ds = New DataSet("Employees") conn = New SqlConnection() conn.ConnectionString = ConnectionString adapter = New SqlDataAdapter(sql, conn) adapter.Fill(ds, "Employees") conn.Close() conn.Dispose() End Sub
The BindListControls method is where you bind the ComboBox and ListBox controls. Listing 7-15 shows the BindListControls method. As you can see, this code binds the ComboBox to the EmployeeID column and binds the three ListBox controls to the FirstName, LastName, and Title columns.
Listing 7-15: Binding ListBox and ComboBox Controls
' Bind data with controls Private Sub BindListControls() ComboBox1.DataSource = ds.Tables(0) ComboBox1.DisplayMember = "EmployeeID" ListBox1.DataSource = ds.Tables(0) ListBox1.DisplayMember = "FirstName" ListBox2.DataSource = ds.Tables(0) ListBox2.DisplayMember = "LastName" ListBox3.DataSource = ds.Tables(0) ListBox3.DisplayMember = "Title" End Sub
If you run the application and select any record in the ComboBox or ListBox controls, you'll see that the other controls select the correct value. For instance, if you select the sixth record in the ComboBox, all of the ListBox controls reflect this choice (see Figure 7-5).
Figure 7-5: Data synchronization in ComboBox and ListBox controls
The DataGrid control is much more powerful than any other data-bound control. It's also capable of displaying data relations. A DataGrid control displays data in a tabular, scrollable, and editable grid. Like other data-bound controls, a DataGrid control can display data from various sources with the help of its DataSource property. The DataSource property can be a DataTable, DataView, DataSet, or DataViewManager.
When a DataSet or a DataViewManager contains data from more than one table, you can specify what table you want to display in the DataGrid property by using the DataMember property. For example, let's say you have a DataSet that contains two tables—Customers and Orders. By default, if you bind a DataSet, it'll display data from both tables. But if you want to display data from the Customers table only, you need to set the DataMember property to the table's name. Listing 7-16 sets the DataSource and DataMember properties of a DataGrid.
Listing 7-16: Setting the DataSource and DataMember Properties of a DataGrid Control
ds = New DataSet() sql = "SELECT * FROM Customers" ds = New DataSet() adapter = New SqlDataAdapter(sql, conn) adapter.Fill(ds) DataGrid1.DataSource = ds DataGrid1.DataMember = "Customers"
You can also set the DataSource and DataMember properties by using the DataGrid control's SetDataBinding method. This method takes the first argument as a dataSource and the second argument as a dataMember. Typically, a data source is a DataSet, and the dataMember is the name of a table available in the DataSet. The following code shows how to call the SetDataBinding method of a DataGrid control:
DataGrid1.SetDataBinding(ds, "Customers")
You'll use the DataGrid control and its members throughout this chapter.
Removing data binding from a data-bound control is simple. The following code snippet deletes data binding from a DataGrid control:
DataGrid1.DataSource = null; DataGrid1.DataMember = "";