WPF DataGrid – Dynamically updating DataGridComboBoxColumn


The Scenario


I want to do a master detail like scenario where the selection in one ComboBox cell will update the list of items in the next ComboBox cell and so on.


 


Setting up the DataSource and ViewModel


I will use the Northwind database for this example and will use the first column to select a Category within the Categories table. 


ComboBoxSample1


The second column will have a ComboBox to select all the Products within the selected Category.


ComboBoxSample2


And the third column will have a ComboBox to select all the orders that were placed on the selected Product.


ComboBoxSample3


The ViewModel that I will use on each DataGrid item will have the properties:


·         CurrentCategory


·          CurrentProduct


·         CurrentOrder


·         ProductsInCategory


·         OrdersFromProduct


Each time CurrentCategory is updated, ProductsInCategory will be updated as well to create the new list of products.  When CurrentProduct is updated, OrdersFromProduct will be updated in a similar fashion.  So for example, CurrentCategory will look like this:



public int CurrentCategory


{       


  get { return _currentCategory; }


  set


  {


      _currentCategory = value;


      ProductsInCategory = DBAccess.GetProductsInCategory(_currentCategory).Tables[“Products”].DefaultView;


      OnPropertyChanged(“CurrentCategory”);


  }


}


 


GetProductsInCategory() does a query on the database passing in the category id.  I will not go over the database querying implementation here.


 


Hooking up to the UI


For the first DataGridComboBoxColumn, the ComboBox.ItemsSource will bind to the Categories DataTable through a StaticResource.  The binding on the column will be set to the CurrentCategory.



<dg:DataGridComboBoxColumn Header=”Current Category”


    SelectedValueBinding=”{Binding Path=CurrentCategory}”


      SelectedValuePath=”CategoryID”


    DisplayMemberPath=”CategoryName”


    ItemsSource=”{Binding Source={StaticResource categoriesDataProvider}}”>


</dg:DataGridComboBoxColumn>


 


The other DataGridComboBoxColumns will have to take a slightly different approach.  Let’s say we do something like this:



<!–NOT CORRECT–>


<dg:DataGridComboBoxColumn Header=”Current Product”


    SelectedValueBinding=”{Binding Path=CurrentProduct}”


    SelectedValuePath=”ProductID”


    DisplayMemberPath=”ProductName”


    ItemsSource=”{Binding Path=ProductsInCategory}”>


</dg:DataGridComboBoxColumn>


 


It does seem more intuitive to take this approach however DataGridColumns are not actually part of the visual tree and only the Binding DPs will actually be set on generated cell elements to inherit DataGrid’s DataContext.  That means that the binding for ItemsSource will fail as it won’t be able to find path, ProductsInCategory, that it is current set. 


Aside:  Jaime wrote this nice post on how to set the DataContext for the columns to DataGrid’s DataContext, but I’m going to show an implementation here with just the default implementation.


So what we want is for the ItemsSource to bind to the DataContext of the row on the DataGrid.  We can do that by setting the binding on the actual generated element of the column through ElementStyle and EditingElementStyle.



<!—now itemssource will find the correct DataContext–>


<dg:DataGridComboBoxColumn Header=”Current Product”


    SelectedValueBinding=”{Binding Path=CurrentProduct}”


      SelectedValuePath=”ProductID”


    DisplayMemberPath=”ProductName”>              


  <dg:DataGridComboBoxColumn.ElementStyle>


    <Style TargetType=”ComboBox”>


      <Setter Property=”ItemsSource” Value=”{Binding Path=ProductsInCategory}” />


    </Style>


  </dg:DataGridComboBoxColumn.ElementStyle>


  <dg:DataGridComboBoxColumn.EditingElementStyle>


    <Style TargetType=”ComboBox”>


      <Setter Property=”ItemsSource” Value=”{Binding Path=ProductsInCategory}” />


    </Style>


  </dg:DataGridComboBoxColumn.EditingElementStyle>


</dg:DataGridComboBoxColumn>


 


This works because internally when the cell is being generated, the Content of the cell will have its layout updated which will make it part of the visual tree and it will have access to the correct DataContext.  Now the ItemsSource for the ComboBox will dynamically update each time CurrentCategory is changed.  The DataGridComboBoxColumn for the CurrentOrder is similar to CurrentProduct so I will not show that here. 


You can download the sample here.

DataGrid_ComboBoxColumnSamples_Dynamic.zip

Comments (11)

  1. Kaschan says:

    Hi vinsibal,

    I would like to use a typed DataTable as a data source of DataGrid instead of ObservableCollection. Could you please show me how to implement it?

    Best Regards.

  2. Kaschan,

    In the sample, one of the windows uses a DataTable.DataView as the data source.  Does that help?

  3. Kaschan says:

    Hi vinsibal,

    Thanks for your reply, anyway it’s not help. I understands that you use DataView as the data source of ComboBox. But my question is about data source of DataGrid.

  4. Margaret says:

    Newbie question:

    WHat if there is more than one row?

    How would one filter the combobox based on another field/property in the same row.

    ie. the combobox itemsource for each row (elementstyle) would have to be different.

    I tried binding the EditingElementStyle to the filtered itemsource, and keep the ElementStyle bound to the non-filtered source – but no matter what I try,

    the text for a selected value on a different row disappears when I change the filter (presumably because that value is no longer present in the current itemsource).  

  5. mplut says:

    DUHHH…Never mind – your solution works fine for all rows…I’ll just have to look a little more carefully.

  6. UPDATE: the WPF DataGrid v1 has just released. For more information, see this post . The information

  7. David Montejo says:

    I’ve trying to populate a DataGridComboBoxColumn using a filtered ObjectDataProvider,

               <ObjectDataProvider x:Key="ingregastobanProvider"

             ObjectInstance="{StaticResource ingregastobanTableAdapter}"

             MethodName="GetData">

                   <ObjectDataProvider.MethodParameters>

                       <sys:String>I</sys:String>

                   </ObjectDataProvider.MethodParameters>

               </ObjectDataProvider>

    Updating the MethodParameter in a MultiBinding make all rows lookup combobox values get changed, not only the actual row…so changing the filtered objectdataprovider seems to change the full datagrid row source.

    Is a correct aproach?

    I would appreciate some help. I’ve been spend 3 days tryinf to figuer it, and don’t get any progress…

  8. Shailendra says:

    Excellent Article! I was looking out for this for so many days. You saved my life!

  9. shailendrasute says:

    Hi

    Tried this and it works great for me.

    I want add one more functionality. When user selects a category and it there are no products related to category then Products will show textbox to when edit and save it as new product.

    Is this possible?

    Thanks again for wonderful article.

    Regards

    Shailendra

  10. Niraj says:

    Nice Post.

    Do any one have idea when when VS 2010 will be released?

  11. Cord says:

    This hint really hellped me out the missing understanding. Thanks