with the DataBindings node expanded to show the binding for the Text
property.
fiGure 27-7 fiGure 27-8
568 .
chaPter 27 dATASeTS And dATAbinding
From the drop-down list you can see that the Text property is being bound to the FirstName field of
the CustomerBindingSource. Because the CustomerBindingSource is bound to the Customer table,
this is actually the FirstName column in that table. If you look at the designer file for the form, you
can see that this binding is set up using a new Binding, as shown in the following snippet:
Me.FirstNameTextBox.DataBindings.Add(
New System.Windows.Forms.Binding("Text",
Me.CustomerBindingSource,
"FirstName", True))
A Binding is used to ensure that two-way binding is set up between the Text field of the
FirstNameTextBox and the FirstName field of the CustomerBindingSource. The controls for
the other controls all have similar bindings between their Text properties and the appropriate fields
on the CustomerBindingSource.
When you run the current application you will notice that the Modified Date value is displayed
as in the default string representation of a date, for example, “13/10/2004.” Given the nature
of the application, it might be more useful to have it in a format similar to “Friday, 13 October
2004.” To do this you need to specify additional properties as part of the DataBinding. Select the
ModifiedDateLabel1 and in the Properties tool window, expand the DataBindings node and select
the Advanced item. This opens up the Formatting and Advanced Binding dialog as shown in Figure 27-9.
fiGure 27-9
In the lower portion of Figure 27-9 you can see that we have selected one of the predefined
formatting types, Date Time. This then presents another list of formatting options in which
“Saturday, 7 November 2009” has been selected — this is an example of how the value will be
formatted. In this dialog we have also provided a Null value, “N/A,” which will be displayed if
there is no Modified Date value for a particular row. In the following code you can see that three
additional parameters have been added to create the DataBinding for the Modified Date value:
Binding Data .
569
Vb
Me.ModifiedDateLabel1.DataBindings.Add(
New System.Windows.Forms.Binding("Text",
Me.CustomerBindingSource,
"ModifiedDate", True,
System.Windows.Forms.DataSourceUpdateMode.OnValidation,
"N/A", "D"))
The OnValidation value simply indicates that the data source will be updated when the visual
control has been validated. This is actually the default and is only specified here so that the next two
parameters can be specified. The “N/A” is the value you specified for when there was no Modified
Date value, and the “D” is actually a shortcut formatting string for the date formatting you selected.
bindingnavigator
Although the CustomerBindingNavigator component, which is an instance of the
BindingNavigator class, appears in the non-visual area of the design surface, it does have a visual
representation in the form of the navigation toolstrip that is initially docked to the top of the form.
As with regular toolstrips, this control can be docked to any edge of the form. In fact, in many ways
the BindingNavigator behaves the same way as a toolstrip in that buttons and other controls can
be added to the Items list. When the BindingNavigator is initially added to the form, a series of
buttons are added for standard data functionality, such as moving to the first or last item, moving to
the next or previous item, and adding, removing, and saving items.
What is neat about the BindingNavigator is that it
not only creates these standard controls, but also wires
them up for you. Figure 27-10 shows the Properties
window for the BindingNavigator, with the Data
and Items sections expanded. In the Data section
you can see that the associated BindingSource is
the CustomerBindingSource, which will be used
to perform all the actions implied by the various
button clicks. The Items section plays an important
role, because each property defines an action, such as
AddNewItem. The value of the property defines the
ToolStripItem to which it will be assigned — in this
case, the BindingNavigatorAddNewItem button.
Behind the scenes, when this application is run and
this button is assigned to the AddNewItem property,
the OnAddNew method is wired up to the Click event of the button. This is shown in the following
snippet, extracted using Reflector from the BindingNavigator class. The AddNewItem property
calls the WireUpButton method, passing in a delegate to the OnAddNew method:
Vb
Public Property AddNewItem As ToolStripItem
Get
If ((Not Me.addNewItem Is Nothing) AndAlso Me.addNewItem.IsDisposed) Then
fiGure 27-10
570 .
chaPter 27 dATASeTS And dATAbinding
Me.addNewItem = Nothing
End If
Return Me.addNewItem
End Get
Set(ByVal value As ToolStripItem)
Me.WireUpButton(Me.addNewItem, value, _
New EventHandler(AddressOf Me.OnAddNew))
End Set
End Property
Private Sub OnAddNew(ByVal sender As Object, ByVal e As EventArgs)
If (Me.Validate AndAlso (Not Me.bindingSource Is Nothing)) Then
Me.bindingSource.AddNew
Me.RefreshItemsInternal
End If
End Sub
Private Sub WireUpButton(ByRef oldButton As ToolStripItem, _
ByVal newButton As ToolStripItem, _
ByVal clickHandler As EventHandler)
If (Not oldButton Is newButton) Then
If (Not oldButton Is Nothing) Then
RemoveHandler oldButton.Click, clickHandler
End If
If (Not newButton Is Nothing) Then
AddHandler newButton.Click, clickHandler
End If
oldButton = newButton
Me.RefreshItemsInternal
End If
End Sub
The OnAddNew method performs a couple of important actions. First, it forces validation of the
active field, which is examined later in this chapter. Second, and the most important aspect of
the OnAddNew method, it calls the AddNew method on the BindingSource. The other properties
on the BindingNavigator also map to corresponding methods on the BindingSource, and it is
important to remember that the BindingSource, rather than the BindingNavigator, does the work
when it comes to working with the data source.
data source selections
Now that you have seen how the BindingSource works, it’s time to improve the user interface. At
the moment, the Sales Person is being displayed as a textbox, but this should actually be limited
to just the sales staff at AdventureWorks. As such, instead of a textbox, it would be much better to
have the list of staff displayed as a drop-down box from which the user can select.
Start by removing the SalesPersonTextBox from the form. Next, add a ComboBox control from
the toolbox. With the new ComboBox selected, note that a smart tag is attached to the control.
Expanding this tag and checking the Use Data Bound Items checkbox opens the Data Binding Mode
options, as shown in Figure 27-11.
Binding Data .
571
fiGure 27-11
You need to define four things to get the DataBinding to work properly. The first is the data source
for the list of staff the user should be able to select from. Unfortunately, the list of staff is not
contained in a database table (this may be the case if the list of staff comes from a separate system
such as Active Directory). For the purpose of this example the list staff is defined by a fixed array of
SalesPerson objects.
Vb
Public Class SalesPerson
Public ReadOnly Property FriendlyName
Get
Return Name.Replace("adventure-works\", String.Empty)
End Get
End Property
Public Property Name As String
Public Shared Function Staff() As SalesPerson()
Return {
New SalesPerson() With {.Name = "adventure-works\pamela0"},
New SalesPerson() With {.Name = "adventure-works\david8"},
New SalesPerson() With {.Name = "adventure-works\jillian0"},
New SalesPerson() With {.Name = "adventure-works\garrett1"},
New SalesPerson() With {.Name = "adventure-works\jae0"},
New SalesPerson() With {.Name = "adventure-works\linda3"},
New SalesPerson() With {.Name = "adventure-works\josé1"},
New SalesPerson() With {.Name = "adventure-works\michael9"},
New SalesPerson() With {.Name = "adventure-works\shu0"}
}
572 .
chaPter 27 dATASeTS And dATAbinding
End Function
End Class
Code snippet SalesPerson.vb
c#
public class SalesPerson{
public string FriendlyName{
get{
return Name.Replace(@"adventure-works\", String.Empty);
}
}
public string Name { get; set; }
public static SalesPerson[] Staff(){
return new SalesPerson[]{
new SalesPerson() {Name= @"adventure-works\pamela0"},
new SalesPerson() {Name= @"adventure-works\david8"},
new SalesPerson() {Name= @"adventure-works\jillian0"},
new SalesPerson() {Name= @"adventure-works\garrett1"},
new SalesPerson() {Name= @"adventure-works\jae0"},
new SalesPerson() {Name= @"adventure-works\linda3"},
new SalesPerson() {Name= @"adventure-works\josé1"},
new SalesPerson() {Name= @"adventure-works\michael9"},
new SalesPerson() {Name= @"adventure-works\shu0"}
};
}
}
Code snippet SalesPerson.cs
Expanding the Data Source drop-down
allows you to select from any of the existing
project data sources. Although the list of
staff, returned by the Staff method on
the SalesPerson class, is contained in the
project, it can’t yet be used as a data source.
First, you need to add a new Object data
source to your project. You can do this
directly from the Data Source drop-down by
selecting the Add Project DataSource link.
This displays the Data Source Configuration
Wizard as you saw earlier in this chapter.
However, this time you will select Object as
the type of data source. You will then have to
select which object(s) you want to include in
the data source, as shown in Figure 27-12.
fiGure 27-12
Binding Data .
573
When you select SalesPerson and click Finish the data source will be created and automatically
assigned to the Data Source property of the Sales Person drop-down. The Display Member and
Value Member properties correspond to which properties on the SalesPerson object you want to be
displayed and used to determine the selected item. In this case, the SalesPerson defines a read-only
property, FriendlyName (which simply removes the adventure-works prefix), which should be
displayed in the drop-down. However, the Value property needs to be set to the Name property so
that it matches the value specified in the SalesPerson field in the Customer table. Lastly, the Selected
Value property needs to be set to the SalesPerson property on the CustomerBindingSource. This is
the property that is get/set to determine the Sales Person specified for the displayed Customer.
Although you have wired up the Sales Person drop-down list, if you run what you currently have,
there would be no items in this list, because you haven’t populated the SalesPersonBindingSource.
The BindingSource object has a DataSource property, which you need to set in order to populate the
BindingSource. You can do this in the Load event of the form:
Vb
Private Sub CustomerForm_Load(ByVal sender As Object,
ByVal e As EventArgs) Handles MyBase.Load
Me.SalesPersonBindingSource.DataSource = SalesPerson.Staff
End SubPrivate
Code snippet CustomersForm.vb
c#
private void CustomerForm_Load(object sender, EventArgs e){
this.salesPersonBindingSource.DataSource = SalesPerson.Staff();
}
Code snippet CustomersForm.cs
Now when you run the application, instead of having a textbox with a numeric value, you have a
convenient drop-down list from which to select the SalesPerson.
saving changes
Now that you have a usable interface, you need to add support for making changes and adding new
records. If you double-click the Save icon on the CustomerBindingNavigator toolstrip, the code
window opens with a code stub that would normally save changes to the Customer table. As you
can see in the following snippet, there are essentially three steps: the form is validated, each of the
BindingSources has been instructed to end the current edit, and then the UpdateAll method is
called on the TableAdapterManager:
Vb
Private Sub CustomerBindingNavigatorSaveItem_Click(ByVal sender As Object,
ByVal e As System.EventArgs) _
Handles CustomerBindingNavigatorSaveItem.Click
Me.Validate()
574 .
chaPter 27 dATASeTS And dATAbinding
Me.CustomerBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.AdventureWorksLTDataSet)
End Sub
c#
private void customerBindingNavigatorSaveItem_Click(object sender, EventArgs e){
this.Validate();
this.customerBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.adventureWorksLTDataSet);
}
This code will run without modification but it won’t update the ModifiedDate field to indicate
the Customer information has changed. You need to correct the Update method used by the
CustomerTableAdapter to automatically update the ModifiedDate field. Using the DataSet
designer, select the CustomerTableAdapter, open the Properties window, expand the
UpdateCommand node, and click the ellipsis button next to the CommandText field. This opens
the Query Builder dialog that you used earlier in this chapter. Uncheck the boxes in the Set column
for the rowguid row (because this should never be updated). In the New Value column, change
@ModifiedDate to getdate() to automatically set the modified date to the date on which the
query was executed. This should give you a query similar to the one shown in Figure 27-13.
fiGure 27-13
With this change, when you save a record the ModifiedDate will automatically be set to the
current date.