Browse DevX
Sign up for e-mail newsletters from DevX


Tackle Complex Data Binding with Windows Forms 2.0 : Page 3

Two-way data binding can save you a ton of coding, as long as you can get the bound controls to behave the way you want them to.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Many-to-Many Relations Data Binding
If you look back at Figure 1, you can see that authors and titles are related to one another through a many-to-many relationship. This is evident in a relational schema like a DataSet because of the need of a join table (titleauthor) that holds foreign keys into the two related tables.

There is no inherent way to automatically synchronize the contents of the two tables (authors and titles) in this case, because it is up to you to decide which table to treat as a parent and which to treat as a child for display purposes. Also, the presence of the intermediate table poses an additional complication. However, BindingSources make this situation relatively simple to tackle as well.

Suppose that you want to show a table of authors, and when each author is selected in the grid, their published titles show up in a second grid in the form (Figure 3). You could achieve this by re-querying the database on each selection, performing a JOIN on the authors, titles, and titleauthor tables for the appropriate au_id. However, that means extra round trips to the server, which is usually unnecessary.

Figure 3: Many-to-many data binding sample.
What we can do is set up a BindingSource for each one of the tables involved (authors, titles, and titleauthor), and use a combination of the automatic synchronization for master-details binding, the events raised by a BindingSource, and the filtering capabilities of a BindingSource to get what you want with minimal code. To support this solution, the underlying data source for the BindingSource will have to support filtering through an implementation of the IBindingList interface. DataSets provide this for you out of the box. If you are using custom object collections, you will have to do a fair amount of extra work to achieve that capability.

Here's what you'll do: The authors grid will have a straightforward binding to the authors table through its BindingSource. Likewise, the titles grid will have a similar binding for the titles table. You'll set up an additional BindingSource for the titleauthor table as a child binding on the authors BindingSource. Then, whenever the list changes for the titleauthor BindingSource (which will happen whenever a new author is selected in the author grid through the master-details mechanisms), you'll set up a new filter criteria for the titles BindingSource to only show the related titles. Listing 2 shows the complete form code that sets all this up.

The Form Load event handler in Listing 2 first loads up the PubsDataSet as described earlier. It then binds the authors and titles grids to their respective BindingSource objects. The authors and titles BindingSources are bound to their respective tables in the DataSet. The m_TitleAuthorBindingSource is bound using a master-details binding as described in the previous section so that the list exposed by that BindingSource will update whenever the selection in the authors BindingSource changes. You can ignore the publishers binding code for now; that will be discussed in the next section.

Finally, the Load event handler wires up some event handlers for events raised by the BindingSource objects. The BindingComplete and ListChanged events are raised by a BindingSource object when the data binding process is complete against its underlying data source, and when the contents of the list it manages have changed, respectively. By hooking up event handlers for these events against the m_TitleAuthorBindingSource, you will be notified whenever the child list of rows in the join table has been updated due to a selection change in the authors table. You could have also done the same against the authors table, instead monitoring the CurrentChanged event.

The event subscriptions are done in Listing 2 using a new C# language feature called delegate inference. You can simply set the event subscription to the name of the handler method, and the compiler will worry about creating an instance of an appropriate delegate to add to the event's subscriber list.

The event handlers just described call a helper method called BindTitles(). This method loops through the rows in master-details bound m_TitleAuthorBindingSource list, and generates a filter string that is then applied to the m_TitlesBindingSource object to restrict which of the rows in its collection it displays.

"Details-Master" Data Binding
You won't hear it called that often, but another common requirement in data binding is to provide a display mechanism for many-to-one relations between collections of data. This is basically synchronizing from a selection in a collection of child items to update another control that will display the corresponding parent item.

The easiest way to do this is to tap into the events raised by a BindingSource again. The code I skipped over in the Load event handler of Listing 2 sets up a data binding between the publishers table and the textbox at the bottom of the form that displays the publisher name for the currently selected title in the titles grid.

m_PublishersBindingSource.DataSource = m_PubsDataSet.publishers; // Set the simple binding on the textbox m_PublisherTextBox.DataBindings.Add("Text", m_PublishersBindingSource, "pub_name");

The Add method on the DataBindings collection of a control creates a new Binding object and adds it to the collection of bindings for that control. In this case, you are tying the pub_name column of the publishers table to the Text property of the TextBox control through the m_PublishersBindingSource as the data source. Whatever item is current in the data source will automatically be displayed in the TextBox, and any edits to the text will be pushed back into the underlying data source when the focus leaves the control.

With just those two lines of code, there is no synchronization set up between selected titles and the publishers. There are a couple of ways to achieve that synchronization. One would be to not even bother with data binding as shown above, but simply set the Text property of the TextBox directly based on row selections in the titles grid. This will trigger the CurrentChanged event on the m_TitlesBindingSource for which you have an event handler. In that event handler, you could just navigate to the parent row using the properties exposed on a typed DataSet, and use it to set the TextBox value.

private void OnCurrentTitleChanged(object sender, EventArgs e) { // Get the current row from the titles source DataRowView rowView = m_TitlesBindingSource.Current as DataRowView; // Cast to the strongly typed row type PubsDataSet.titlesRow row = rowView.Row as PubsDataSet.titlesRow; // Set the value — no data binding m_PublisherTextBox.Text = row.publishersRow.pub_name; }

However, if you want to stick with a data bound approach, you can again use the filtering capabilities of BindingSources to filter the publishers list down to only the matching row.

private void OnCurrentTitleChanged(object sender, EventArgs e) { // Get the current row from the titles source DataRowView rowView = m_TitlesBindingSource.Current as DataRowView; // Cast to the strongly typed row type PubsDataSet.titlesRow row = rowView.Row as PubsDataSet.titlesRow; // Set the filter on the publishers source m_PublishersBindingSource.Filter = "pub_id = '" + row.pub_id + "'"; }

Now the publisher name is constantly kept synchronized through data binding and filtering.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date