Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Master-Detail Binding

4.5. Master-Detail Binding

We've seen binding to a single object. We've seen binding to a single list of objects. One other 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 liste.g., customer 452sets the filtering parameters for the associated detail datae.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 4-47.

Example 4-47. Master-detail data for binding
public class Families : ObservableCollection<Family> {}

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 People : ObservableCollection<Person> {}

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; }
  }
}


In Example 4-47, 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 4-19.

In Figure 4-19, 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 4-48.

Figure 4-19. Sample master-detail data


Example 4-48. Declaring sample master-detail data
<!-- Window1.xaml -->
<?Mapping
  XmlNamespace="local" ClrNamespace="MasterDetailBinding" ?>
<Window ... xmlns:local="local">
  <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="Moe" Age="22" />

            <local:Person Name="Curly" Age="23" />
          </local:People>
        </local:Family.Members>
      </local:Family>
      <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>
  ...
</Window>

Binding to this data at the top leveli.e., to show the family namescould look like Example 4-49.

Example 4-49. Binding to master Family data
<!-- Window1.xaml -->
<?Mapping ... ?>
<Window ...>
  <Window.Resources>
    <local:Families x:Key="Families">...</local:Families>

  </Window.Resources>
  <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 TextContent="{Binding Path=FamilyName}" />

        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
</Window>

In Example 4-49, 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 4-20.

Figure 4-20. Showing family data


Figure 4-20 isn't master-detail, of course, since selecting a master family doesn't show its associated details. To do that, we need to bind to the next level, as in Example 4-50.

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

  ...
  <!-- Families Column -->
  ...
  <!-- Members Column -->
  <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
    <TextBlock TextContent="{Binding Path=FamilyName}" />

    <TextBlock TextContent=" Family Members:" />
  </StackPanel>
  <ListBox Grid.Row="1" Grid.Column="1"
    IsSynchronizedWithCurrentItem="True"
    ItemsSource="{Binding Path=Members}" >
    <ListBox.ItemTemplate>

      <DataTemplate>
        <StackPanel Orientation="Horizontal">
          <TextBlock TextContent="{Binding Path=Name}" />
          <TextBlock TextContent=" (age: " />
          <TextBlock TextContent="{Binding Path=Age}" />

          <TextBlock TextContent=" )" />
        </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 4-21 shows master-detail binding in action.

Figure 4-21. Showing master Family and detail Person data


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, as in Example 4-51.

Example 4-51. Adding a 3rd 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 which consist of people with names and ages, but each person also has a set of traits, each with their own description. Expanding our XAML a bit to include traits would look like Example 4-52.

Example 4-52. Declaring a third level of detail
<local:Families x:Key="Families">
  <local:Family FamilyName="Stooge">
    <local:Family.Members>

      <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="Moe" Age="22" >...</local:Person>

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

With a third level of detail, we bind as shown in Example 4-53.

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

  <!-- Families Column -->

  ...

  <!-- Members Column -->
  ...

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

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

      <DataTemplate>
        <TextBlock TextContent="{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 ... TextContent="{Binding Path=FamilyName}" />


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

<TextBlock ... TextContent="{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 ...
  TextContent="{Binding Path=Members/Name}" />


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

<TextBlock ...
  TextContent="{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 tri-level master-detail example looks like Figure 4-22.

Figure 4-22. Showing master-detail-more detail data



©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions