Whereas simple binding to list data sources binds a control property to a property on the current object of a list data source, complex binding binds a control property to an entire list data source. The "complex" in complex binding refers to the additional support that's built into controls themselves to operate over and render list data. For example, when you bind a ListBox control to a list data source, the ListBox automatically hooks up the relevant bindings and adds each object in the list data source to its list of items to display: // ComplexBindingListBoxForm.cs partial class ComplexBindingListBoxForm: Form { // Create strongly typed list data source List<RaceCarDriver> raceCarDrivers = new List<RaceCarDriver>(); public ComplexBindingListBoxForm () { // Populate list data source with data items this.raceCarDrivers.Add(new RaceCarDriver("M. Schumacher", 500)); this.raceCarDrivers.Add(new RaceCarDriver("A. Senna", 1000)); this.raceCarDrivers.Add(new RaceCarDriver("A. Prost", 400)); // Simple-bind the Name and Wins properties to the Name and // Wins text boxes. Keeps the RaceCarDriver object up-to-date as // user enters data this.nameTextBox.DataBindings.Add( "Text", this.raceCarDrivers, "Name"); this.winsTextBox.DataBindings.Add( "Text", this.raceCarDrivers, "Wins"); // Complex-bind list box to RaceCarDriver's list data source this.raceCarDriversListBox.DataSource = this.raceCarDrivers; // Specify the property whose value will appear in the list box // for each item in the list data source this.raceCarDriversListBox.DisplayMember = "Name"; ... } ... // Navigation code ... } There are three interesting aspects to this code. First, the list box is bound to the list data source via the list box's DataSource property. Complex controls usually expose DataSource to consume list data sources. Second, the list box's DisplayMember property is set to specify which property of the contained objects provides the value that's displayed in the list; by default, this value is retrieved from the ToString property of each contained object, but list box controls allow customization via the DisplayMember property. Third, all three controls are bound to the same list data source, and this means that they share the same binding manager by default and, therefore, share the same idea of which object is current, as Figure 16.9 demonstrates. Figure 16.9. Complex-Bound ListBox and Simple-Bound TextBox Controls Sharing the Same Current Object
At this point, our sample UI supports navigating through a list of RaceCarDriver objects. It doesn't, however, support the addition of new RaceCarDriver objects, or even the deletion of existing ones. We could either update our UI to provide these abilities or use a DataGridView control, which has this additional support built in. The updated form would look like Figure 16.10. Figure 16.10. DataGridView Complex-Bound to RaceCarDriver's List Data Source
With a DataGridView dropped onto a form at design time, the form shown in Figure 16.10 is enabled with the following code: // ComplexBindingDataGridViewForm.cs public partial class ComplexBindingDataGridViewForm : Form { // Create strongly typed list data source List<RaceCarDriver> raceCarDrivers = new List<RaceCarDriver>(); public ComplexBindingDataGridViewForm() { // Populate list data source with data items this.raceCarDrivers.Add(new RaceCarDriver("M. Schumacher", 500)); this.raceCarDrivers.Add(new RaceCarDriver("A. Senna", 1000)); this.raceCarDrivers.Add(new RaceCarDriver("A. Prost", 400)); // Complex-bind list box to RaceCarDriver's list data source this.racingCarDriversDataGridView.DataSource = this.raceCarDrivers; } // Navigation code ... } As you can see, the code is slightly slimmer, but we have gained some additional support: First, the DataGridView can peer into a list data source to determine the type of items it contains and, for each public property, dynamically create a column on our behalf. Second, the DataGridView lets us edit items in-place, including support for adding and deleting items. void addButton_Click(object sender, EventArgs e) { // Add item to list data source directly RaceCarDriver raceCarDriver = new RaceCarDriver("Nelson Piquet", 300); this.raceCarDrivers.Add(raceCarDriver); // Select new item this.BindingManager.Position = this.BindingManager.Count - 1; } private void deleteButton_Click(object sender, EventArgs e) { // Remove item from list data source directly this.raceCarDrivers.Remove( (RaceCarDriver)this.BindingManager.Current); } Unfortunately, this code has no immediate visual effect: DataGridView neither displays the newly added RaceCarDriver object nor shows the effects of its removal.[3] Figure 16.11 illustrates.
Figure 16.11. DataGridView Not Automatically Reflecting List Data Source ChangesThe reason is that a list data source of type List<T> doesn't provide notification of actions taken against its list items, such as adding, updating, and deleting, and thus the data binding engine doesn't see them. Therefore, just as item data sources need to implement a communications protocol to notify simple-bound controls of change, list data sources need to implement a communications protocol to let both simple-bound and complex-bound clients know about both list and item changes. This protocol is embodied by the IBindingList interface. |