0
votes

I am developing a wpf application based on MVVM. All I am doing is trying to bind Nested DataGrids with my DataTable. I have class CustomTable

public class CustomTable : INotifyPropertyChanged
{
        public List<DataTable> Main { get; set; }
        public CustomTable Child { get; set; } 
        public DataRowView _selectedItem;
        public DataRowView SelectedItem
        {
            get
            {
                return _selectedItem;
            }
            set
            {
                _selectedItem = value;
                Child = new CustomTable();
                OnPropertyChanged("SelectedItem");
            }
        }  

        public CustomTable()
        {
            Main = new List<DataTable>();
            Main.Add(someRandomTable());

        }

        private DataTable someRandomTable()
        {
            DataTable table = new DataTable();
            table.Columns.Add("Dosage", typeof(int));
            table.Columns.Add("Drug", typeof(string));
            table.Columns.Add("Patient", typeof(string));
            table.Columns.Add("Date", typeof(DateTime));
            table.Rows.Add(25, "Indocin", "David", DateTime.Now);
            table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now);
            table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now);
            table.Rows.Add(21, "Combivent", "Janet", DateTime.Now);
            table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now);
            return table;
        }



        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string caller)
        {

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(caller));
            }
        }

}

Custom Table consist of List of DataTables -> Main, SelectedItem, and Child of CustomTable type. I am implementing INotifyPropertyChanged to this class.

So I have ItemsControl whose DataContext is this class and ItemSource binds to Main. And My ItemControl.Template consist of DataGrid. On running it successfully displays no of DataGrids binded to each element in Main List. Now I am binding the SelectedItem property of DataGrid to SelectedItem property of CustomTable. I am able to do this also. now when I select a row and the setter of SelectedItem of customTable is called I am creating new instance of Child with predefined Main in it (For sample only, the actual logic is complex and is not related to the question). Now I want to bind my Child to RowDetailsTemplate of my selectesRow so that Nested dataGrids are displayed. This should be recurssive like, clicking a row in Child's DataGrid should also display Child->Child->Main. I am not able to achieve this and trying for last few days with no progress. Any other approach other then this is also welcomed.

Edit

XAML File

<Window x:Class="HierDataGrid.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:tvcc="clr-namespace:HierDataGrid"
    Title="MainWindow" Height="350" Width="525" xmlns:metro="http://schemas.codeplex.com/elysium">
<Window.Resources>
    <DataTemplate x:Key="Nested">

        <ItemsControl ItemsSource="{Binding DataContext.Tables, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                    <DataGrid CanUserAddRows="False" RowDetailsTemplate="{DynamicResource Nested}" ItemsSource="{Binding Main}" AutoGenerateColumns="True" >
                    </DataGrid>
                </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
    </DataTemplate>
</Window.Resources>


<ScrollViewer DataContext="{Binding}">
    <StackPanel>

        <ItemsControl ItemsSource="{Binding Path=TableCollection.Main}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                    <DataGrid Name="dg" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" CanUserAddRows="False" RowDetailsTemplate="{StaticResource Nested}"   ItemsSource="{Binding}" AutoGenerateColumns="True" >

                    </DataGrid>
                </DataTemplate>
        </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>
</ScrollViewer>

1
is TableCollection is of type CustomTable? - pushpraj
appreciated if you could also post a picture of your expected output. - pushpraj

1 Answers

1
votes

I did try to make a recursive template based on some assumptions

screen shot

screen

I did some changes in your class too, implemented notification changed to Child property

    private CustomTable _child;
    public CustomTable Child
    {
        get
        {
            return _child;
        }
        set
        {
            _child = value;
            OnPropertyChanged("Child");
        }
    }

others can be done when required

xaml

<ScrollViewer>
    <ScrollViewer.Resources>
        <DataTemplate DataType="{x:Type l:CustomTable}">
            <StackPanel>
                <ItemsControl ItemsSource="{Binding Path=Main}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <DataGrid SelectedItem="{Binding DataContext.SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor,AncestorType=ItemsControl}}"
                                      CanUserAddRows="False"  ItemsSource="{Binding}" AutoGenerateColumns="True" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                <Expander Header="Child" Margin="10" IsExpanded="True" x:Name="child">
                    <ContentControl Content="{Binding Child}"/>
                </Expander>
            </StackPanel>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Child}" Value="{x:Null}">
                    <Setter TargetName="child" Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ScrollViewer.Resources>

    <ContentControl Content="{Binding TableCollection}"/>

</ScrollViewer>

l: in above example refer to the name space to the class CustomTable, replace the your xaml with this. also you may remove the templates in the window resources too.

I have also restructured the templates

  • Removed the named templates and created a data template for CustomTable.
  • Used ContentControl to bind to TableCollection and WPF will resolve our defined template for the same.
  • Same will apply to the ContentControl in the DataTemplate which is bound to the Child

so this will create a recursive template as in screen shot

you may adjust the template as per your needs. do please ask if you need any clarification.

Row level child

xaml

<ScrollViewer>
    <ScrollViewer.Resources>
        <DataTemplate DataType="{x:Type l:CustomTable}">
            <StackPanel>
                <ItemsControl ItemsSource="{Binding Path=Main}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <DataGrid Name="dg" SelectedItem="{Binding DataContext.SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor,AncestorType=ItemsControl}}"
                                  CanUserAddRows="False"  ItemsSource="{Binding}" AutoGenerateColumns="True" >
                                <DataGrid.RowDetailsTemplate>
                                    <DataTemplate>
                                        <ContentControl Margin="10"
                                                        Content="{Binding DataContext.Child, RelativeSource={RelativeSource FindAncestor,AncestorType=ItemsControl,AncestorLevel=2}}"/>
                                    </DataTemplate>
                                </DataGrid.RowDetailsTemplate>
                            </DataGrid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </DataTemplate>
    </ScrollViewer.Resources>

    <ContentControl Content="{Binding TableCollection}"/>

</ScrollViewer>

result

result