digitig
digitig

Reputation: 2130

WPF DataGrids for hierarchical information

I have an application that contains an ObservableCollection<Foo>, and Foo in turn contains an ObservableCollection<Bar>. I would like a pair of datagrids, one showing the collection of Foo objects in the application and the other showing the collection of Bar objects in the Foo that's currently selected in the first datagrid (and I want to be able to add, update and delete entries in both datagrids).

So far I've got the following XAML:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <DataGrid Grid.Column="0" ItemsSource="{Binding Foos}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Foo Name" Binding="{Binding Name}" Width="Auto" IsReadOnly="False" />
            </DataGrid.Columns>
        </DataGrid>
        <GridSplitter HorizontalAlignment="Right" VerticalAlignment="Stretch" Grid.Column="1" ResizeBehavior="PreviousAndNext" Width="5" Background="Gray" />
        <DataGrid Grid.Column="2">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Bar Name" Width="Auto" IsReadOnly="False"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

And the following code:

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

namespace Test
{
    public class Foo
    {
        static int _nextId;
        public int Id { get; private set; }
        public String Name { get; set; }
        public ObservableCollection<Bar> Bars { get; set; }
        public Foo()
        {
            Id = _nextId++;
            Name = String.Empty;
            Bars = new ObservableCollection<Bar>();
        }
    }

    public class Bar
    {
        static int _nextId;
        public int Id { get; private set; }
        public String Name { get; set; }
        public Bar()
        {
            Id = _nextId++;
            Name = String.Empty;
        }
    }
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<Foo> Foos { get; set; }
        public MainWindow()
        {
            Foos = new ObservableCollection<Foo>();
            Foo newFoo;
            for (int index = 0; index < 5; ++index)
            {
                newFoo = new Foo();
                newFoo.Name = String.Format("Foo {0}", index);
                Foos.Add(newFoo);
            }
            InitializeComponent();
            DataContext = this;
        }
    }
}

Obviously I've not bound the 2nd DataGrid yet, because I've not got the faintest idea how to do it! All the examples I can find assume I'm binding DataTables, not custom objects, and bind to a relation on the DataTables. I don't really understand binding all that well yet. Can anybody tell me how to bind the 2nd table?

(And yes, if you've seen my other recent questions, I am giving WPF another shot after not getting on well with it in the early days).

Thanks in advance.

Upvotes: 0

Views: 2920

Answers (2)

yo chauhan
yo chauhan

Reputation: 12295

Hi If you want editable grids first of all you will have to implement INotifyPropertyChanged like

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

public class ViewModel 
{
    public ViewModel()
    {
        Foos = new ObservableCollection<Foo>();
    }

    public ObservableCollection<Foo> Foos { get; set; }
}

public class Foo : INotifyPropertyChanged
{
    static int _nextId;
    public int Id { get; private set; }
    public ObservableCollection<Bar> Bars { get; set; }
    public Foo()
    {
        Id = _nextId++;
        Name = String.Empty;
        Bars = new ObservableCollection<Bar>();
    }
    private string name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            Notify("Name");
        }
    }

    private void Notify(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

public class Bar : INotifyPropertyChanged
{
    static int _nextId;
    public int Id { get; private set; }
    public Bar()
    {
        Id = _nextId++;
        Name = String.Empty;
    }

    private string name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            Notify("Name");
        }
    }

    private void Notify(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

In xaml Binding for first Grid is corrrect and for second grid you can set ItemsSource as the the selectedItem of First grid using ElementName

<DataGrid Grid.Column="2" ItemsSource="{Binding ElementName=gridTop, Path=SelectedItem.Bars}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Bar Name" Binding="{Binding Name}" Width="Auto" IsReadOnly="False"/>
        </DataGrid.Columns>
    </DataGrid>

Upvotes: 1

paparazzo
paparazzo

Reputation: 45096

Use Binding to Element
Name the top grid gridTop

DataContext="{Binding ElementName=gridTop, Path=SelectedItem.Bars}"

Upvotes: 1

Related Questions