Winforms Data Binding Lessons Learned

Displaying Many-to-Many Relationships

Displaying a master-details relation in a form is really not a big deal. You set up one control, which is bound to the master table, and one DataGrid, which is bound to the relation between the master table and the child table like so:

adpSoldiers.Fill(ds, "Soldiers")
ds.Relations.Add("SoldierNotes", ds.Tables("Soldiers").Columns("ID"),
    ds.Tables("Notes").Columns("SoldierID"))

In this code, we have one table, Soldiers and for each soldier we can have one or more notes, which is a simple one-to-many relationship. All we have to do in order to display a master details form here is have two DataGrids, one bound to ds.Tables("Soldiers") and one bound to ds with a DataMember property of "Soldiers.SoldierNotes".

The problem arises when we have a many-to-many relationship. Let's say we have another table named Courses.” Each soldier can be enrolled into one or more courses, and each course can be taken by one or more soldiers. For that purpose, we have a third table, Registrations,” which contains only SoldierID and CourseID as columns with a PK comprised of both of these columns. Suppose we want to show, for the selected soldier in grid 1, all the courses, including course name, in which he is enrolled. Simple data binding to the relation between soldiers and registrations will not do.

adpSoldiers.Fill(ds, "Soldiers")
adpSoldiers.Fill(ds, "Courses")
adpSoldiers.Fill(ds, "Registrations")
ds.Relations.Add("SolCourses", ds.Tables("Soldiers").Columns("ID"),
    ds.Tables("Registrations ").Columns("SoldierID"))

We get unreadable data in the Details grid only showing SoldierID and CourseID columns. So what's the solution? Unfortunately, as much as I looked, there is not really an elegant solution, but here's the best way I've found to overcome this. We can add calculated columns into our Registrations data table, which, for each data row in the table, displays the value of the parent Courses row's Name value, using a predefined relation.

To accomplish this, we first add this relation into our relations collection, which enables us to retrieve the parent Courses row for each Registrations row according to the CourseID value:

ds.Relations.Add("CourseReg", ds.Tables("Courses").Columns("ID"),
    ds.Tables("Registrations ").Columns("CourseID"))

Next, we add the calculated columns to our Registrations data table:

ds.Tables("Registrations").Columns.Add("Course", GetType(String), "Parent(CourseReg).Name")

What I pass in to the Columns constructor is the name of the new columns, the type of displayed data in those columns, and the expression that the columns should hold. To retrieve the parent row using a specified relation, I write Parent(RelationName).ColumnName. I could also get a child aggregate function using this data column, by specifying Count(Child(RelationsName).ID). If the DataTable only has one relation defined, I don't even have to specify the name of the relation to use, like so: Sum(Child.Price). To learn more about calculated columns, here's a nice article about the subject.

Now that we have our calculated column in place, we can use the same relation in the details grid, only this time we see the name of the course as well as the other columns. The calculated columns in the DataGrid are read-only, as they should be. A new problem is encountered, though. The user still cannot add new items to the DataGrid, without knowing in advance the ID of the needed course and the soldier. Once those values are entered inside the new row in the grid, the Name column displays the name of the new course registration, but not before then. For that you either have to have a combo box that is placed over the CourseID Active cell, or other, more user-friendly external means of inserting a row to the data table.

You might also like...

Comments

About the author

Roy Osherove Israel

Roy Osherove has spent the past 6+ years developing data driven applications for various companies in Israel. He's acquired several MCP titles, written a number of articles on various .NET topic...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The trouble with programmers is that you can never tell what a programmer is doing until it's too late.” - Seymour Cray