Posted by: Zeeshan Amjad | September 13, 2009

Master Detail Data Binding in WPF using LINQ


We have already studied some data binding example. Now we are going to explore the master detail data binding. Here we are going to discuss data binding from two prospective. We are going to study how we can bind more than one control with the same data set. For this purpose we are going to bind the combo box as well as few textblock with the data items.

In our example we have a class of student. This class store some attributes of the students such as ID, first name, last name and grade. In combo box we display only the last name of the student. But in the bottom of the screen we display rest of the information of the selected student. For this purpose we select the path of all text block as a selected items of the combo box. Here is a piece of XAML code to demonstrate this.

  1: <StackPanel DataContext="{Binding ElementName=cmbName, Path=SelectedItem}">
  2:     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=ID}"/>
  3:     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=FirstName}"/>
  4:     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=LastName}"/>
  5:     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=Grade}"/>
  6: </StackPanel>
  7: 

At the same time we also want to get data from other data set whenever user changes the selection in the combo box. For this purpose we are using LINQ to get data from another record set.

For simplicity we created two record set in the form of array and maintain their master detail relationship. The same is true if we want to get the data from any other source such as database. Here is our data we are using in this example with master detail relationship between them.

  1: Student[] studentClass = { new Student(5, "Bob", "Smith", "Passed"),
  2:         new Student(10, "Alex", "Martin", "Passed"),
  3:         new Student(12, "Joe", "Brown" , "Failed"),
  4:         new Student(13, "Ralph", "Ronald" , "Failed"),
  5:         new Student(16, "David", "Frank", "Passed"),
  6:         new Student(18, "Patrick", "Justin", "Failed"),
  7:         new Student(22, "Roy", "Williams", "Failed"),
  8:         new Student(25, "Ken", "White", "Passed")};
  9: 
 10: Subjects[] subjectsClass = { new Subjects("MA111", "PreCalculus", 5),
 11:                                new Subjects("EN101", "English Composition", 5),
 12:                                new Subjects("CIS101", "Computer", 5),
 13:                                new Subjects("EC201", "Macro Economics", 10),
 14:                                new Subjects("EC201", "Macro Economics", 12),
 15:                                new Subjects("MA210", "Calculus 1", 12),
 16:                                new Subjects("MA206", "Statistics", 13),
 17:                                new Subjects("PS101", "General Psychology", 14),
 18:                                new Subjects("ACCT101", "Accounting 1", 16),
 19:                                new Subjects("BU103", "Introduction to Business", 16),
 20:                                new Subjects("MA111", "PreCalculus", 16),
 21:                                new Subjects("EC201", "Macro Economics", 16),
 22:                                new Subjects("EC202", "Micro Economics", 18),
 23:                                new Subjects("MA211", "Calculus 2", 18),
 24:                                new Subjects("BI101", "General Biology", 22),
 25:                                new Subjects("CH101", "General Chemistry", 22),
 26:                                new Subjects("En101", "English Composition", 25),
 27:                                new Subjects("LA100", "Introduction to Law", 25)};
 28: 

Here student and subjects are simple classes just store the information. But we can also add the business logic here. Now we have to add the message handler on the selection change of the combo box. Whenever there is selection change in the combo box, we execute the LINQ query to get the data from other data set (in our example subjects array). Here is a code to perform this operation.

  1: private void cmbName_SelectionChanged(object sender, SelectionChangedEventArgs e)
  2: {
  3:     lstSubjects.Items.Clear();
  4: 
  5:     int studentID = ((Student)cmbName.SelectedItem).ID;
  6: 
  7:     IEnumerable<Subjects> selectedSubjects = from fn in subjectsClass
  8:                                              where fn.StudentID == studentID
  9:                                              select fn;
 10: 
 11:     foreach (Subjects subject in selectedSubjects)
 12:     {
 13:         lstSubjects.Items.Add(subject);
 14:     }
 15: }
 16: 

This might not be an optimal way to implement the master detail data binding when record sets are very large. One advantage of this technique is that when we are handling data from the database then we don’t have to store all the record set in memory. LINQ query will get the subset of the detail data at the cost of execution the query every time we change the selection in the combo box. Here is the complete XAML code of this program.

  1: <Window x:Class="LINQWPF.Main"
  2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4:     Title="LINQ and WPF" Height="400" Width="600" Loaded="Window_Loaded">
  5:     <Grid Background="Wheat">
  6:         
  7:         <Grid.RowDefinitions>
  8:             <RowDefinition/>
  9:             <RowDefinition/>
 10:         </Grid.RowDefinitions>
 11:         
 12:         <Grid.ColumnDefinitions>
 13:             <ColumnDefinition/>
 14:             <ColumnDefinition/>
 15:         </Grid.ColumnDefinitions>
 16:         
 17:         <StackPanel Grid.Row="0" Grid.Column="0">
 18:             <TextBlock Margin="5" FontSize="12">
 19:                 Student Last Name
 20:             </TextBlock>
 21:             <ComboBox Name="cmbName" Margin="5" ItemsSource="{Binding}"
 22:                       IsSynchronizedWithCurrentItem="True"
 23:                       HorizontalContentAlignment="Stretch" SelectionChanged="cmbName_SelectionChanged">
 24:                 <ComboBox.ItemTemplate>
 25:                     <DataTemplate>
 26:                         <StackPanel>
 27:                             <TextBlock Margin="5, 0" FontSize="12" Text="{Binding Path=LastName}"/>
 28:                         </StackPanel>
 29:                     </DataTemplate>
 30:                 </ComboBox.ItemTemplate>
 31:             </ComboBox>
 32:         </StackPanel>
 33:         
 34:         <StackPanel Grid.Row="1" Grid.Column="0">
 35:             <TextBlock Margin="5" FontSize="12">
 36:                 Student Detail
 37:             </TextBlock>
 38:             <Border Margin="5" BorderBrush="Brown" BorderThickness="3" CornerRadius="5">
 39:                 <StackPanel DataContext="{Binding ElementName=cmbName, Path=SelectedItem}">
 40:                     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=ID}"/>
 41:                     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=FirstName}"/>
 42:                     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=LastName}"/>
 43:                     <TextBlock Margin="5" FontSize="12" Text="{Binding Path=Grade}"/>
 44:                 </StackPanel>
 45:             </Border>
 46:         </StackPanel>
 47:         
 48:         <DockPanel Grid.Row="0" Grid.Column="1" Grid.RowSpan="2">
 49:             <TextBlock DockPanel.Dock="Top" Margin="5" FontSize="12">
 50:                 Subjects
 51:             </TextBlock>
 52:             <ListBox Name="lstSubjects" Margin="5" HorizontalContentAlignment="Stretch">
 53:                 <ListBox.ItemTemplate>
 54:                     <DataTemplate>
 55:                         <Border Margin="5" BorderBrush="Brown" BorderThickness="3" CornerRadius="5">
 56:                             <StackPanel>
 57:                                 <TextBlock Margin="5, 0" FontFamily="12" Text="{Binding Path=ID}"/>
 58:                                 <TextBlock Margin="5, 0" FontFamily="12" Text="{Binding Path=Name}"/>
 59:                             </StackPanel>
 60:                         </Border>
 61:                     </DataTemplate>
 62:                 </ListBox.ItemTemplate>
 63:             </ListBox>
 64:         </DockPanel>
 65:     </Grid>
 66: </Window>
 67: 

And here is the complete C# code of this.

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Windows;
  5: using System.Windows.Controls;
  6: using System.Xml.Linq;
  7: 
  8: namespace LINQWPF
  9: {
 10:     /// <summary>
 11:     /// Interaction logic for Main.xaml
 12:     /// </summary>
 13:     public partial class Main : Window
 14:     {
 15:         Student[] studentClass = { new Student(5, "Bob", "Smith", "Passed"),
 16:                 new Student(10, "Alex", "Martin", "Passed"),
 17:                 new Student(12, "Joe", "Brown" , "Failed"),
 18:                 new Student(13, "Ralph", "Ronald" , "Failed"),
 19:                 new Student(16, "David", "Frank", "Passed"),
 20:                 new Student(18, "Patrick", "Justin", "Failed"),
 21:                 new Student(22, "Roy", "Williams", "Failed"),
 22:                 new Student(25, "Ken", "White", "Passed")};
 23: 
 24:         Subjects[] subjectsClass = { new Subjects("MA111", "PreCalculus", 5),
 25:                                        new Subjects("EN101", "English Composition", 5),
 26:                                        new Subjects("CIS101", "Computer", 5),
 27:                                        new Subjects("EC201", "Macro Economics", 10),
 28:                                        new Subjects("EC201", "Macro Economics", 12),
 29:                                        new Subjects("MA210", "Calculus 1", 12),
 30:                                        new Subjects("MA206", "Statistics", 13),
 31:                                        new Subjects("PS101", "General Psychology", 14),
 32:                                        new Subjects("ACCT101", "Accounting 1", 16),
 33:                                        new Subjects("BU103", "Introduction to Business", 16),
 34:                                        new Subjects("MA111", "PreCalculus", 16),
 35:                                        new Subjects("EC201", "Macro Economics", 16),
 36:                                        new Subjects("EC202", "Micro Economics", 18),
 37:                                        new Subjects("MA211", "Calculus 2", 18),
 38:                                        new Subjects("BI101", "General Biology", 22),
 39:                                        new Subjects("CH101", "General Chemistry", 22),
 40:                                        new Subjects("En101", "English Composition", 25),
 41:                                        new Subjects("LA100", "Introduction to Law", 25)};
 42:         public Main()
 43:         {
 44:             InitializeComponent();
 45:         }
 46: 
 47:         private void Window_Loaded(object sender, RoutedEventArgs e)
 48:         {
 49:             foreach (Student student in studentClass)
 50:             {
 51:                 cmbName.Items.Add(student);
 52:             }
 53:         }
 54: 
 55:         private void cmbName_SelectionChanged(object sender, SelectionChangedEventArgs e)
 56:         {
 57:             lstSubjects.Items.Clear();
 58: 
 59:             int studentID = ((Student)cmbName.SelectedItem).ID;
 60: 
 61:             IEnumerable<Subjects> selectedSubjects = from fn in subjectsClass
 62:                                                      where fn.StudentID == studentID
 63:                                                      select fn;
 64: 
 65:             foreach (Subjects subject in selectedSubjects)
 66:             {
 67:                 lstSubjects.Items.Add(subject);
 68:             }
 69:         }
 70:     }
 71: 
 72:     public class Student
 73:     {
 74:         public Student(int id, 
 75:             String firstName, 
 76:             String lastName,
 77:             String grade)
 78:         {
 79:             ID = id;
 80:             FirstName = firstName;
 81:             LastName = lastName;
 82:             Grade = grade;
 83:         }
 84: 
 85:         public int ID
 86:         { get; set; }
 87: 
 88:         public String FirstName
 89:         { get; set; }
 90: 
 91:         public String LastName
 92:         { get; set; }
 93: 
 94:         public String Grade
 95:         { get; set; }
 96:     }
 97: 
 98:     public class Subjects
 99:     {
100:         public Subjects(String id,
101:             String name, int studentId)
102:         {
103:             ID = id;
104:             Name = name;
105:             StudentID = studentId;
106:         }
107: 
108:         public String ID
109:         { get; set; }
110: 
111:         public String Name
112:         { get; set; }
113: 
114:         public int StudentID
115:         { get; set; }
116:     }
117: }
118: 

Here is the output of this program when user select student whose last name is “Frank”.

Linq_Wpf

Advertisements

Responses

  1. […] between two list boxes here. We also saw one example of master detail data binding using LINQ here. This time we are going to implement the master detail relationship between tree and list view […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: