none
Databinding for nested ListView

    Question

  • I have a ListView (using GridView) bound to a DataTable. I use CellTemplates in the GridView to point to DataTemplates in my page resources. This works fine.

     

    Next I tried to add a new column which would have a DataTemplate that would itself define another (nested) ListView. This nested ListView also uses the same CellTemplate/DataTemplate approach as the first one. I can run this and see the parent ListView data, and in the "sub-report" column, I can see the nested ListView headers for each parent row.

     

    Now, my problem is that I can't find a way to access these nested listviews in order to bind data to them. I define all of the templates in XAML but I'm doing the binding programmatically when the page loads. This is easy with the parent ListView since it is exposed as a variable in the codebehind, but I can't figure out how to access the nested listviews.

     

    BTW - I want to bind each nested ListView to a different DataView with different filters based on values of the parent row. I'm doing this programmatically when the page loads via several related DataTables in a DataSet. If there is a declarative way to do this in XAML, I'd also be interested, but I'm primarially after the programmatic solution.

     

    Here is an example of the XAML:

    Code Snippet

    <Page.Resources>

    <DataTemplate x:Key="MyParentColumn_CellTemplate">

    <StackPanel>

    <TextBlock Text="{Binding Path=MyParentColumn}" />

    </StackPanel>

    </DataTemplate>

    <DataTemplate x:Key="MyChildColumn_CellTemplate">

    <StackPanel>

    <TextBlock Text="{Binding Path=MyChildColumn}" />

    </StackPanel>

    </DataTemplate>

    <DataTemplate x:Key="SubReportCellTemplate">

    <StackPanel>

    <ListView>

    <ListView.View>

    <GridView>

    <GridViewColumn CellTemplate="{StaticResource MyChildColumn_CellTemplate}" Header="My Child Column"/>

    </GridView>

    </ListView.View>

    </ListView>

    </StackPanel>

    </DataTemplate>

    </Page.Resources>

     

    <ListView Name="lvwReport">

    <ListView.View>

    <GridView>

    <GridViewColumn CellTemplate="{StaticResource MyParentColumn_CellTemplate}" Header="My Parent Column"/>

    </GridView>

    </ListView.View>

    </ListView>

     

     

    And here is an example of the code I'm using to bind from the constructor of the codebehind:

    Code Snippet

    //Bind the parent listview

    lvwReport.ItemsSource = _reportDataSet.Tables["MyParentTable].DefaultView;

    lvwReport.DataContext = _reportDataSet;

     

    //Bind each nested listview to create sub reports

    foreach (object item in lvwReport.Items)

    {

    //?? How can I access the ListView for each item in order to bind it?

    //?? I'd like to get each item and create a different DataView with filter

    //?? based on the value of the current item (parent row)

     

    //?? E.g.,:

    DataRow parentRow = ((DataRowView)item).Row;
    DataView dv = new DataView(_reportDataSet.Tables["MyChildTable"], "ParentID = " + parentRow["ParentID"].ToString(), "", DataViewRowState.None);
    //Bind nested ListView to DataView here

    }

     

     

    Any help would be appreciated. Thanks.
    Monday, August 20, 2007 4:43 PM

Answers


  • Hi there,

    I created a simple project to show you how i would do this...

    so lets start from the XAML

    Code Snippet

    <Window x:Class="TestCompositeCollection.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
            >
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.9*"/>
                <RowDefinition Height="0.1*"/>
            </Grid.RowDefinitions>

            <Grid.Resources>
                <DataTemplate x:Key="SubReportCellTemplate">
                    <ListView ItemsSource="{Binding Path=Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}} }" >
                        <ListView.View>
                            <GridView>
                                <GridViewColumn DisplayMemberBinding="{Binding Test}" Header="My Child Column"/>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </DataTemplate>
            </Grid.Resources>

            <ListView Name="testList" Grid.Row="0">
                <ListView.View>
                    <GridView>
                        <GridViewColumn DisplayMemberBinding="{Binding Test}" Header="test" />
                        <GridViewColumn CellTemplate="{StaticResource SubReportCellTemplate}" Header="test2" />
                    </GridView>
                </ListView.View>
            </ListView>
           
        </Grid>
    </Window>


    as you can see I am using the data template you are using. I did a small change to bind the item source of the listview inside the data template to the TAG property of the listview item...

    now in the code I do the following

    Code Snippet

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Collections.ObjectModel;
    using System.ComponentModel;

    namespace TestCompositeCollection
    {
        public class A
        {
            string test;
            public string Test { get { return test; } set { test = value; } }

            public A(string a)
            {
                test = a;
            }
        }

        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();

                ObservableCollection<A> list1 = new ObservableCollection<A>();
                for (int i = 0; i < 9; i++)
                {
                    list1.Add(new A(i.ToString()));
                }

                testList.ItemsSource = list1;

                testList.Loaded += new RoutedEventHandler(testList_Loaded);
            }

            void testList_Loaded(object sender, RoutedEventArgs e)
            {
                ObservableCollection<A> list = new ObservableCollection<A>();
                for (int i = 0; i < 9; i++)
                {
                    list.Add(new A(String.Format("Child{0}", i.ToString())));
                }

                foreach (object item in testList.ItemsSource)
                {
                    ListViewItem uiItem = testList.ItemContainerGenerator.ContainerFromItem(item)
                        as ListViewItem;
                    uiItem.Tag = list;
                }
            }
        }
    }


    what I am doing in the code is get the ListViewItem object that are in the main listview and then set the Tag property foreach one of them....


    I hope that this helps...

    Regards

    Monday, August 20, 2007 5:29 PM

All replies


  • Hi there,

    I created a simple project to show you how i would do this...

    so lets start from the XAML

    Code Snippet

    <Window x:Class="TestCompositeCollection.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
            >
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.9*"/>
                <RowDefinition Height="0.1*"/>
            </Grid.RowDefinitions>

            <Grid.Resources>
                <DataTemplate x:Key="SubReportCellTemplate">
                    <ListView ItemsSource="{Binding Path=Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}} }" >
                        <ListView.View>
                            <GridView>
                                <GridViewColumn DisplayMemberBinding="{Binding Test}" Header="My Child Column"/>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </DataTemplate>
            </Grid.Resources>

            <ListView Name="testList" Grid.Row="0">
                <ListView.View>
                    <GridView>
                        <GridViewColumn DisplayMemberBinding="{Binding Test}" Header="test" />
                        <GridViewColumn CellTemplate="{StaticResource SubReportCellTemplate}" Header="test2" />
                    </GridView>
                </ListView.View>
            </ListView>
           
        </Grid>
    </Window>


    as you can see I am using the data template you are using. I did a small change to bind the item source of the listview inside the data template to the TAG property of the listview item...

    now in the code I do the following

    Code Snippet

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Collections.ObjectModel;
    using System.ComponentModel;

    namespace TestCompositeCollection
    {
        public class A
        {
            string test;
            public string Test { get { return test; } set { test = value; } }

            public A(string a)
            {
                test = a;
            }
        }

        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();

                ObservableCollection<A> list1 = new ObservableCollection<A>();
                for (int i = 0; i < 9; i++)
                {
                    list1.Add(new A(i.ToString()));
                }

                testList.ItemsSource = list1;

                testList.Loaded += new RoutedEventHandler(testList_Loaded);
            }

            void testList_Loaded(object sender, RoutedEventArgs e)
            {
                ObservableCollection<A> list = new ObservableCollection<A>();
                for (int i = 0; i < 9; i++)
                {
                    list.Add(new A(String.Format("Child{0}", i.ToString())));
                }

                foreach (object item in testList.ItemsSource)
                {
                    ListViewItem uiItem = testList.ItemContainerGenerator.ContainerFromItem(item)
                        as ListViewItem;
                    uiItem.Tag = list;
                }
            }
        }
    }


    what I am doing in the code is get the ListViewItem object that are in the main listview and then set the Tag property foreach one of them....


    I hope that this helps...

    Regards

    Monday, August 20, 2007 5:29 PM
  • Thank you for the detailed reply.

     

    This was exactly what I was looking for. The key was the ListView.ItemContainerGenerator.ContainerFromItem() method and then using the ListViewItem tag for binding.

     

    Monday, August 20, 2007 7:30 PM
  • Hey, your sample is very good and useful. But if I wanted to find a selected Item for one of the nested listViews during a button click event how would I do that?

     

    private void btnMatch_Click(object sender, RoutedEventArgs e)
    {

    // loop through all items in the outer listView to access thier inner listViews

    for (int i = 0; i < lstMatchItems.Items.Count; i++)
    {

    // get the current listViewItem's content

    ItemsMatch matchItem = (ItemsMatch)lstMatchItems.ItemsIdea;

     

    // if this item is of a matchable type
    if (matchItem.ItemMatchType == ItemMatchType.PotentialMatch
        || matchItem.ItemMatchType == ItemMatchType.NoMatch)
    {

         // get the item as a ListViewItem
         ListViewItem listViewItem = (ListViewItem)lstMatchItems.ItemContainerGenerator.ContainerFromIndex(i);

         // get the bound DataItem for the inner ListView
         object o = listViewItem.Tag;

     

         // find the selected Item of the inner listview???

         // this is where I am having trouble, how do I access this inner listview and get it's selected Items?
    }

    }

    }

    Tuesday, April 22, 2008 11:30 AM