Nested Data

Oct 3, 2008 at 2:38 PM
Edited Oct 3, 2008 at 2:40 PM
I'm having trouble figuring out how to bind data grid columns to nested data. I have some classes that define a data structure:

using System.Collections.ObjectModel;

namespace DataGridTest
{
  public class Contact
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public ObservableCollection<Address> Addresses { get; set; }
  }

  public class Address
  {
    public string Line1 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
  }
}



I initially wanted to try nesting a datagrid inside of a DataGridTemplateColumn (see code below), but couldn't get the Addresses collection to bind to the nested datagrid. So I tried something simpler - try to display the Addresses collection in a DataGridComboBoxColumn, but couldn't figure out how to bind the Addresses collection to that column, either.

Here is the window I'm working with:


<Window
  x:Class="DataGridTest.GridTest"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
  x:Name="gridTest"
  Title="Test"
  >
 
  <StackPanel>
    <!-- Trying to get a combobox to work... -->
    <dg:DataGrid x:Name="comboBoxTest" Height="150" Margin="5" ItemsSource="{Binding ElementName=gridTest, Path=Contacts}">
      <dg:DataGrid.Columns>
        <dg:DataGridTextColumn DataFieldBinding="{Binding FirstName}" Header="First Name" />
        <dg:DataGridTextColumn DataFieldBinding="{Binding LastName}" Header="Last Name" />
        <dg:DataGridComboBoxColumn DataFieldBinding="{Binding Line1}" Header="Addresses" ItemsSource="{Binding ElementName=gridTest, Path=Contacts.Addresses}"/>
      </dg:DataGrid.Columns>
    </dg:DataGrid>
   
    <!-- Trying to get an embedded data grid to work... -->
    <dg:DataGrid x:Name="dataGridTest" Height="150" Margin="5" ItemsSource="{Binding ElementName=gridTest, Path=Contacts}">
      <dg:DataGrid.Columns>
        <dg:DataGridTextColumn DataFieldBinding="{Binding FirstName}" Header="First Name" />
        <dg:DataGridTextColumn DataFieldBinding="{Binding LastName}" Header="Last Name" />
        <dg:DataGridTemplateColumn Header="Addresses">
          <dg:DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
              <dg:DataGrid ItemsSource="{Binding ElementName=gridTest, Path=Contacts.Addresses}">
                <dg:DataGridTextColumn DataFieldBinding="{Binding Line1}" Header="Address Line 1" />
                <dg:DataGridTextColumn DataFieldBinding="{Binding City}" Header="City" />
                <dg:DataGridTextColumn DataFieldBinding="{Binding State}" Header="State" />
                <dg:DataGridTextColumn DataFieldBinding="{Binding Zip}" Header="Zip" />
              </dg:DataGrid>
            </DataTemplate>
          </dg:DataGridTemplateColumn.CellTemplate>
        </dg:DataGridTemplateColumn>
      </dg:DataGrid.Columns>
    </dg:DataGrid>
  </StackPanel>
</Window>



and here's the code-behind:


using System.Collections.ObjectModel;
using System.Windows;

namespace DataGridTest
{
  public partial class GridTest
  {
    public GridTest()
    {
      InitializeComponent();
      GrabData();
    }

    public ObservableCollection<Contact> Contacts
    {
      get { return (ObservableCollection<Contact>)GetValue(ContactsProperty); }
      set { SetValue(ContactsProperty, value); }
    }

    public static readonly DependencyProperty ContactsProperty =
        DependencyProperty.Register("Contacts", typeof(ObservableCollection<Contact>), typeof(GridTest));

    private void GrabData()
    {
      Contacts = new ObservableCollection<Contact>
      {
        new Contact
        {
          FirstName = "Jane",
          LastName = "Doe",
          Addresses = new ObservableCollection<Address>
          {
            new Address
            {
              Line1 = "22 State St.",
              City = "Macon",
              State = "GA",
              Zip = "31201"
            },
            new Address
            {
              Line1 = "685 Fulton St.",
              City = "Atlanta",
              State = "GA",
              Zip = "30301"
            }
          }
        },
        new Contact
        {
          FirstName = "Jack",
          LastName = "Smith",
          Addresses = new ObservableCollection<Address>
          {
            new Address
            {
              Line1 = "48 Drury Ln.",
              City = "Stowe",
              State = "VT",
              Zip = "05672"
            },
            new Address
            {
              Line1 = "685 Fulton St.",
              City = "Atlanta",
              State = "GA",
              Zip = "30301"
            }
          }
        }
      };
    }
  }
}




I know this is probably data-binding 101, but any help getting this binding to work is greatly appreciated!

Rick Martin

Coordinator
Oct 6, 2008 at 2:50 PM
If you want to display a current address as well like what it appears that you are trying to do in your example you should include a current address in the Contact info as well.  For example, have a PrimaryAddress as the current item.  With that you can do something like this:

<

 

dg:DataGridComboBoxColumn DataFieldBinding="{Binding Path=PrimaryAddress.Line1}" DataFieldTarget="Text">
    <dg:DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="ComboBox">
            <Setter Property="ItemsSource" Value="{Binding Path=Addresses}" />
            <Setter Property="DisplayMemberPath" Value="Line1" />
        </Style>
    </dg:DataGridComboBoxColumn.EditingElementStyle>
</dg:DataGridComboBoxColumn>

 

Oct 6, 2008 at 5:30 PM
Yes, thank you. I was also thinking the same thing, but got stumped by the problem above. I really like the DataGrid, but our company needs the ability to display nested data frequently within our next-gen application. This appears to be a shortcoming with the DataGrid compared to 3rd party alternatives out there (or an opportunity to get creative if you don't care for what's out there now...), so I wanted to explore the abilities of the control.