Master Detail Binding

We've seen binding to a single object. We've seen binding to a single list of objects. Another very popular thing to do is to bind to more than one list, especially related lists. For example, if you're showing your users a list of customers and then, when they select one, you'd like to show that customer's related orders, you'll want master-detail binding.

Master-detail binding is a form of filtering, where the selection in the master list (e.g., customer 452) sets the filtering parameters for the associated detail data (e.g., orders for customer 452).

In our discussion thus far, we don't have customers and orders, but we do have families and people, which we could further formalize as shown in Example 7-44.

Example 7-44. Master-detail data for binding public class Person { string name; public string Name { get { return name; } set { name = value; }

int age;

public int Age { get { return age; } set { age = value; }

public class People : ObservableCollection<Person> {}

public class Family { string familyName; public string FamilyName { get { return familyName; } set { familyName = value; }

People members; public People Members { get { return members; } set { members = value; }

public class Families : ObservableCollection<Family> {}

In Example 7-44, we've got our familiar Person class with Name and Age properties, collected into a familiar People collection. Further, we have a Family class with a FamilyName property and a Members property of type People. Finally, we have a Families collection, which collects Family objects. In other words, families have members, which consist of people with names and ages.

You could imagine instances of Families, Family, People, and Person that looked like Figure 7-21.


.Name = "Stooges" .Members

Family .Name = "Addams" .Members




People (Details)




Figure 7-21. Example master-detail data

In Figure 7-21, the Families collection forms the master data, holding instances of the Family class, each of which holds a Members property of type People, which holds the detail Person data. You could populate instances of these data structures as shown in Example 7-45.

Example 7-45. Declaring example master-detail data <!-- Windowl.xaml -->

<Window ... xmlns:local="clr-namespace:MasterDetailBinding"> <Window.Resources> <local:Families x:Key="Families"> <local:Family FamilyName="Stooge"> <local:Family.Members> <local:People> <local:Person Name="Larry" Age="21" /> <local:Person Name="Curly" Age="22" /> <local:Person Name="Moe" Age="23" /> </local:People> </local:Family.Members> </local:Family>

Example 7-45. Declaring example master-detail data (continued)

<local:Family FamilyName="Addams"> <local:Family.Members> <local:People> <local:Person Name="Gomez" Age="135" /> <local:Person Name="Morticia" Age="121" /> <local:Person Name="Fester" Age="137" /> </local:People> </local:Family.Members> </local:Family> </local:Families> </Window.Resources>

Binding to this data at the top level (i.e., to show the family names) could look like Example 7-46.

Example 7-46. Binding to master Family data

<!-- Windowl.xaml --> <Window ...> <Window.Resources> <local:Families x:Key="Families">...</local:Families>


<Grid DataContext="{StaticResource Families}"> <!-- Families Column -->

<TextBlock Grid.Row="0" Grid.Column="0">Families:</TextBlock> <ListBox Grid.Row="1" Grid.Column="0" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}"> <ListBox.ItemTemplate> <DataTemplate>

<TextBlock Text="{Binding Path=FamilyName}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Window>

In Example 7-46, we're setting two things in the Families column (column 0). The first is the header, which is set to the constant string Families. The second forms the body, which is a list of Family objects in the Families collection, showing each family's FamilyName property, as shown in Figure 7-22.

Figure 7-22 isn't master-detail yet, because selecting a master family doesn't show its associated details. To do that, we need to bind to the next level, as shown in Example 7-47.

[¡1 MasterDetailBinding L1^ i£l


Stooge Addams

Figure 7-22. Showing family data

Example 7-47. Binding to detail Person data <Grid DataContext="{StaticResource Families}">

<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal"> <TextBlock Text="{Binding Path=FamilyName}" />

<TextBlock Text=" Family Members:" /> </StackPanel>

<ListBox Grid.Row="1" Grid.Column="1" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Members}" > <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Path=Name}" /> <TextBlock Text=" (age: " /> <TextBlock Text="{Binding Path=Age}" /> <TextBlock Text=" )" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

In the Members column (column 1), we're also setting a header and body, but this time the header is bound to the FamilyName of the currently selected Family object.

Also, recall that in the Families column, our listbox's items source was bound to the entire collection via a Binding statement without a Path. In the details case, however, we want to tell the data binding engine that we'd like to bind to the Members property of the currently selected Family object, which is itself a collection of Person objects. Figure 7-23 shows master-detail binding in action.

But wait; there's more! Master-detail binding doesn't stop at just two levels, oh no. You can go as deep as you like, with each detail binding becoming the master binding for the next level. To see this in action, let's add one more level of detail to our data classes (see Example 7-48).

[Ti| MactprTlptailRinrling [ O I 0 S3 J

Families: Addams Family Members:


Gomez (age: 135)


Morticia {age: 121) Fester (age: 137)

Figure 7-23. Showing master Family and detail Person data

Example 7-48. Adding a third level of detail public class Person { string name; public string Name { get { return name; } set { name = value; }

int age;

public int Age { get { return age; } set { age = value; }

Traits traits; public Traits Traits { get { return traits; } set { traits = value; }

public class Traits : ObservableCollection<Trait> {}

public class Trait { string description; public string Description { get { return description; } set { description = value; }

Now, not only do families have family names and members that consist of people with names and ages, but each person also has a set of traits, each with its own description. Expanding our XAML a bit to include traits would look like Example 7-49.

Example 7-49. Declaring a third level of detail

<local:Families x:Key="Families"> <local:Family FamilyName="Stooge"> <local:Family.Members>

Example 7-49. Declaring a third level of detail (continued)

<local:People> <local:Person Name="Larry" Age="21"> <local:Person.Traits> <local:Traits> <local:Trait Description="In Charge" /> <local:Trait Description="Mean" /> <local:Trait Description="Ugly" /> </local:Traits> </local:Person.Traits> </local:Person>

<local:Person Name="Curly" Age="22" >...</local:Person>

</local:People> </local:Family.Members>



With a third level of detail, we bind as shown in Example 7-50.

Example 7-50. Binding to a third level of detail data <Grid DataContext="{StaticResource Families}">

<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal"> <TextBlock Text="{Binding Path=Members/Name}" />

<TextBlock Text=" Traits:" /> </StackPanel>

<ListBox Grid.Row="1" Grid.Column="2" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Members/Traits}" > <ListBox.ItemTemplate> <DataTemplate>

<TextBlock Text="{Binding Path=Description}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid>

In the case of the Families column header, recall that we had no binding at all; the text was hardcoded:

<TextBlock ...>Families:</TextBlock>

In the case of the Members column header, we bound to the FamilyName of the currently selected Family object like so:

<TextBlock ... Text="{Binding Path=FamilyName}" />

Logically, you could think of that as expanding to the following:

<TextBlock ... Text="{Binding Path=family.FamilyName}" /> where family is the currently selected Family object.

Taking this one level deeper, in the case of the traits column header, we're binding to the Name property of the currently selected Person from the Members property of the currently selected Family, which binds like this:

<TextBlock ... Text="{Binding Path=Members/Name}" />

Again, logically you could think of it expanding like this:

<TextBlock ... Text="{Binding Path=family.Members.person.Name}" />

where family is the currently selected Family object and person is the currently selected Person object. The / in the binding statement acts as the separator between objects, with the object at each level assumed to be "currently selected."

The binding for the listbox's items source works the same way, except we want the Traits collection from the currently selected Person, not the Name. Our trilevel masterdetail example looks like Figure 7-24.

■ 1 MasterDetailBinding

Jii= I«1 £3 1


Stooge Family Members:

_arry Traits:


Larry [age: 21)

In Charge


Moe (age: 22)


Curly (age: 23)


Figure 7-24. Showing master-detail-more detail data

Was this article helpful?

0 0
Project Management Made Easy

Project Management Made Easy

What you need to know about… Project Management Made Easy! Project management consists of more than just a large building project and can encompass small projects as well. No matter what the size of your project, you need to have some sort of project management. How you manage your project has everything to do with its outcome.

Get My Free Ebook

Post a comment