{"id":51,"date":"2009-02-17T23:11:14","date_gmt":"2009-02-18T03:11:14","guid":{"rendered":"http:\/\/joemorrison.org\/blog\/?p=51"},"modified":"2009-02-17T23:11:14","modified_gmt":"2009-02-18T03:11:14","slug":"excedrin-headache-35401281-using-combo-boxes-with-the-wpf-datagrid","status":"publish","type":"post","link":"https:\/\/morrison.today\/blog\/2009\/02\/17\/excedrin-headache-35401281-using-combo-boxes-with-the-wpf-datagrid\/","title":{"rendered":"Excedrin headache #3.5.40128.1: Using combo boxes with the WPF DataGrid"},"content":{"rendered":"<p><a href=\"https:\/\/morrison.today\/blog\/wp-content\/uploads\/2009\/02\/combobox-example.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/morrison.today\/blog\/wp-content\/uploads\/2009\/02\/combobox-example.png\" alt=\"\" title=\"Combo box example\" width=\"300\" height=\"300\" class=\"alignright size-medium wp-image-52\" srcset=\"https:\/\/morrison.today\/blog\/wp-content\/uploads\/2009\/02\/combobox-example.png 300w, https:\/\/morrison.today\/blog\/wp-content\/uploads\/2009\/02\/combobox-example-150x150.png 150w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a>If you&#8217;re working with the WPF DataGrid (January 2009 release, version 3.5.40128.1) and want to display a column of combo boxes (e.g. to select a value from a list of options) you may be in for a headache, especially if you want the available options to be supplied from the binding context.  For example say you are displaying an editable table of orders, and each order is for a particular quantity of a particular product, and the products are selectable using combo boxes. <\/p>\n<p>Here is a solution, the main element of which came from a <a href=\"http:\/\/www.codeplex.com\/wpf\/WorkItem\/View.aspx?WorkItemId=8153\">posting on the Codeplex web site<\/a> from someone called <a href=\"http:\/\/www.codeplex.com\/site\/users\/view\/XIU\">XIU<\/a> for which I&#8217;m grateful. I thought I would add some more detail in case anyone else has this problem and wants a more cut and pasty solution.<br \/>\n<!--more--><br \/>\nSay you have a class called <em>Product<\/em>, each instance of which has an <em>ID<\/em> and a <em>Description<\/em>, and a class called <em>Order<\/em>, each instance of which has a <em>Quantity<\/em> and a <em>Product ID<\/em> (to be used like a foreign key to designate the order&#8217;s product).<\/p>\n<p>These classes must implement the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms229614.aspx\">INotifyPropertyChanged interface<\/a>. Here are the first few lines of each:<\/p>\n<p><code>public class Product : INotifyPropertyChanged<br \/>\n{<br \/>\n&nbsp;&nbsp;public int ID { ... }<br \/>\n&nbsp;&nbsp;public string Description { ... }<br \/>\n&nbsp;&nbsp;...<br \/>\n}<br \/>\n&nbsp;<br \/>\npublic class Order : INotifyPropertyChanged<br \/>\n{<br \/>\n&nbsp;&nbsp;public int ProductID { ... }<br \/>\n&nbsp;&nbsp;public int Quantity { ... }<br \/>\n&nbsp;&nbsp;...<br \/>\n}<\/code><\/p>\n<p>Create a simple ViewModel (i.e. binding context) and set the DataContext of the main window to an instance of this object:<\/p>\n<p><code>namespace ComboBoxDemo<br \/>\n{<br \/>\n&nbsp;&nbsp;public class ViewModel<br \/>\n&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;public ObservableCollection&lt;Order&gt; Orders { get; set; }<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;public ObservableCollection&lt;Product&gt; Products { get; set; }<br \/>\n&nbsp;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;public ViewModel()<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Products = new ObservableCollection&lt;Product&gt;();<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Products.Add(new Product(100, \"Widget\"));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Products.Add(new Product(101, \"Sprocket\"));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Products.Add(new Product(102, \"Gadget\"));<br \/>\n&nbsp;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Orders = new ObservableCollection&lt;Order&gt;();<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Orders.Add(new Order(102, 15));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Orders.Add(new Order(102, 32));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Orders.Add(new Order(100, 42));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;}<br \/>\n}<\/code><\/p>\n<p>Create a helper class using XIU&#8217;s solution:<\/p>\n<p><code>namespace ComboBoxDemo<br \/>\n{<br \/>\n&nbsp;&nbsp;public class DataGridComboBoxColumnWithBindingHack : DataGridComboBoxColumn<br \/>\n&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FrameworkElement element = base.GenerateEditingElement(cell, dataItem);<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CopyItemsSource(element);<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return element;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FrameworkElement element = base.GenerateElement(cell, dataItem);<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CopyItemsSource(element);<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return element;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;private void CopyItemsSource(FrameworkElement element)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BindingOperations.SetBinding(element, ComboBox.ItemsSourceProperty,<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BindingOperations.GetBinding(this, ComboBox.ItemsSourceProperty));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;}<br \/>\n}<\/code><\/p>\n<p>This is a variant of the <em>DataGridComboBoxColumn<\/em> class which handles binding differently to allow the <em>ItemsSource<\/em> property to have access to the binding context. Now put it all together with the following XAML:<\/p>\n<p><code>&lt;Window x:Class=\"ComboBoxDemo.Window1\"<br \/>\n&nbsp;&nbsp;xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"<br \/>\n&nbsp;&nbsp;xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"<br \/>\n&nbsp;&nbsp;xmlns:wpf=\"http:\/\/schemas.microsoft.com\/wpf\/2008\/toolkit\"<br \/>\n&nbsp;&nbsp;xmlns:local=\"clr-namespace:ComboBoxDemo\"<br \/>\n&nbsp;&nbsp;Title=\"Window1\" Height=\"300\" Width=\"300\"&gt;<br \/>\n&nbsp;&nbsp;&lt;wpf:DataGrid ItemsSource=\"{Binding Orders}\" AutoGenerateColumns=\"False\"&gt;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&lt;wpf:DataGrid.Columns&gt;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;wpf:DataGridTextColumn Header=\"Quantity\" Binding=\"{Binding Quantity}\" \/&gt;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;local:DataGridComboBoxColumnWithBindingHack<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Header=\"Product\"<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SelectedValueBinding=\"{Binding ProductID}\"<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SelectedValuePath=\"ID\"<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DisplayMemberPath=\"Description\"<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ItemsSource=\"{Binding RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}, Path=DataContext.Products}\" \/&gt;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&lt;\/wpf:DataGrid.Columns&gt;<br \/>\n&nbsp;&nbsp;&lt;\/wpf:DataGrid&gt;<br \/>\n&lt;\/Window&gt;<\/code><\/p>\n<p>The <em>SelectedValueBinding<\/em> attribute indicates the Order property containing the foreign key designating the product (i.e. which value should be edited by the combo box). The <em>SelectedValuePath<\/em> attribute indicates which property within the product object contains the product ID, and the <em>DisplayMemberPath<\/em> attribute indicates which property should actually be displayed in the grid. Finally the <em>ItemsSource<\/em> attribute indicates how to get the list of products by navigating to the parent window object and looking up the list of products from the parent window&#8217;s binding context. <\/p>\n<p>Here is the complete project source as a <a href=\"http:\/\/joemorrison.org\/downloads\/combobox-demo.zip\">downloadable zip file<\/a>.<\/p>\n<p>I hope this saves someone some time, and that it&#8217;s easier to do all of this in future versions of the WPF Toolkit. If anyone has a better solution to this problem, please let me know. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re working with the WPF DataGrid (January 2009 release, version 3.5.40128.1) and want to display a column of combo boxes (e.g. to select a value from a list of options) you may be in for a headache, especially if you want the available options to be supplied from the binding context. For example say [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[26,25,24],"class_list":["post-51","post","type-post","status-publish","format-standard","hentry","category-net","tag-combobox","tag-datagrid","tag-wpf"],"blocksy_meta":"","_links":{"self":[{"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/posts\/51","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/comments?post=51"}],"version-history":[{"count":11,"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/posts\/51\/revisions"}],"predecessor-version":[{"id":63,"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/posts\/51\/revisions\/63"}],"wp:attachment":[{"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/media?parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/categories?post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/morrison.today\/blog\/wp-json\/wp\/v2\/tags?post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}