Datagrid binding to list<> contained in a list<>

Mar 3, 2009 at 4:45 PM

 I have a list of objects say recipients, where each object - recipient, contains a property wich is another list of objects say products. Each product has its own properties like description and quantity.  products are added to all the recipients in the list by checking products out in a separate checkboxlist. What I wnat to do is: bind the list of recipeints to a datagrid in a way that the datagrid would dispaly basic information about recipients (name, surname, address) in the firs few columns, the rest of the columns would be created dynamically as one would checkout different products in checkboxlist. the column's header would be bound to product's  description, and the values presented in the column to quantity of the product. 

I can't find a way how to do this kind of binding.

Thank you for your help.
Best regards
Mar 3, 2009 at 7:01 PM
Hello Jurij,

There is no XAML only solution for this. You need to handle CheckBox.IsChecked change of the checkbox list and add/removes columns to/from DataGrid accordingly.

Following is an example for adding such a column....

DataGridTextColumn dgtc = new DataGridTextColumn();
//Set all the needed properties like width etc.
//Let 'index' be the index of object in products list for which to create the column
dgtc.Header = products[index].Description;
dgtc.Binding = new Binding("products[" + index + "].Quantity");
//set display index if needed

For perf reasons it would be good if you could batch adding of these columns.
Mar 3, 2009 at 7:24 PM

Thank you for a quick reply.

I have done almost the same in my code. However I don't see exactly how your solution solves my problem. I don't see how does your list of products relates to products that recipients have asigned to their list property products. I don't know how to get the list products out of the recipients and bind it to the created columns. Each recipient has a list of products, The list of products is the same for all recipients displayed in my datagrid. I add/remove a products to each recipient's list of products as checkboxes get checked/unchecked (I iterate through the list of recipients and add/remove products). The problem I find is that I want to display these reciepents with their products in the same  datagrid and be able to edit the quantity properyt for each product for each recipient. I want my row in the datagrid to look like this:

|name|surename|address|quantity of product1|quantity of product2|...|quantity of productN|

|.| represents columns, whereas number of products can vary.

Best regards
Mar 4, 2009 at 5:42 PM
If the index of the product in productlist is known and will remain same during the life time of DataGrid, you can bind to elements of the list using its index. The following binding should work for you...

dgtc.Binding = new Binding("products[" + index + "].Quantity"); //where index is 0 for product1, 1 for product2 etc.

So effectively the path of binding would be "products[0].Quantity" for 1st element and so on.
Mar 4, 2009 at 7:37 PM

I have tried your solution. I paste the source code below. I can get different products displayed, I can enter a values to a cell, but when that cell looses focus all the cells in that column get updated to that cell.

Here is my solution:

I add Name, surname and checkbox column in my XAML code like this:
 <my:DataGrid Name="dgvProductsWPF" Margin="10,0,10,0" Background="White" CanUserAddRows="False" RowHeaderWidth="21" AutoGenerateColumns="False" AlternatingRowBackground="LightGray" EnableColumnVirtualization="True">
        <my:DataGridTextColumn Width="SizeToCells" Header="Name" Binding="{Binding Name}"/>
        <my:DataGridTextColumn Width="SizeToCells" Header="Surename" Binding="{Binding Surename}"/>
        <my:DataGridCheckBoxColumn Width="SizeToCells" Header="Chosen" Binding="{BindingChosen}"/>

Than in my cb_Checked event handler I handel a chekcbox checked event:

void cb_Checked(object sender, RoutedEventArgs e)
        Product productBinding = new Product() { Quantity = 0, Description = sender.Tag.toString() }; // create new instance of a product to add. This instance created just to keep track of products added to the recipient. The list is kept in a separate list lstproducts
        lstProducts.Add(productBinding); //add the newly checked product to the list od products whcih has a private scope for wpf form I work on.
        int i = lstNarocil.IndexOf(productBinding); // check the index of the added product. The order of the added products is kept the same as in the list belonding to each recipient

(Recipient recipient1 in lstRecipients) // iterate through the list of recipients to add them a new product
              Product product1 = new Product() { Quantity = 0, Produkt = produkt }; //for each recipient create a new instance of the newly added product
             recipient1..ListProducts.Add(product1); //add the instance of product to the recipient

         dgvNarocilaWPF.ItemsSource = null//remove the old datasource for the datagrid 

myBinding = new Binding("ListProducts[" + i + "].Quantity"); //set  the property to bind to
        myBinding.Mode =
BindingMode.TwoWay;  //allow datagrid to transmit updates back to the datasource
        myBinding.Source = lstRecipients;  //define the datasource - list of recipients
clm = new DataGridTextColumn();  //create new column
        clm.Binding = myBinding;  //define binding for the new column
clm.Header = productBinding.Description; //name the new column 
        dgvNarocilaWPF.Columns.Add(clm); //add the new column 

       dgvNarocilaWPF.ItemsSource = lstPrejemnikov;  //to update the look of the datagrid
    catch (Exception)

Thnak you for your advice.


Mar 5, 2009 at 10:02 PM
You should not set myBinding.Source to lstRecipients. myBinding will use DataContext (i.e, Recipient of that row). Hence no need to set any Source.
Mar 5, 2009 at 11:05 PM

I just can not believe it!!! I comment one line - THE line and it works!!! :)

Best Regards