Wednesday, October 12, 2016

MVVM: DataGridComboBoxColumn binding doesn't pick data

This is not a new problem, but I faced this recently. The WPF DataGrid control has DataGridComboBoxColumn which, when bound to a viewmodel collection, doesn't pick up the data.

Here is the viewmodels I created for the demo app.

And the types of the properties are:

    public class MainViewModel : ViewModel
    {
        ObservableCollection<PersonViewModel> _Data;
        ...

    public class PersonViewModel : ViewModel
    {
        string _Name;
        int _Age;
        PersonViewModel _SelectedSibling;
        ObservableCollection<PersonViewModel> _Siblings;
        ...

The binding would seem innocuous, but it doesn't work.
        <DataGrid AutoGenerateColumns="False"
                  ItemsSource="{Binding Data}">
            <DataGrid.Columns>
                ...
                <DataGridComboBoxColumn DisplayMemberPath="Name"
                                        Header="Siblings"
                                        ItemsSource="{Binding Siblings}"
                                        SelectedItemBinding="{Binding SelectedSibling}">
                </DataGridComboBoxColumn>
                ...
            </DataGrid.Columns>
        </DataGrid>


You would expect the combobox to be filled with the names of the siblings, but it doesn't.

So, I looked up and found the answer on StackOverflow:
DataGridColumn doesn't derive from FrameworkElement or FrameworkContentElement so it isn't in the visual tree and doesn't have a DataContext and that's why your binding is failing.
That means the binding on the column's ItemsSource itself won't work. We need to access the combobox in the column and create the binding there.

Here is the working XAML code:

        <DataGrid AutoGenerateColumns="False"
                  ItemsSource="{Binding Data}">
            <DataGrid.Columns>
                ...
                <DataGridComboBoxColumn DisplayMemberPath="Name"
                                        Header="Siblings"
                                        ItemsSource="{Binding Siblings}"
                                        SelectedItemBinding="{Binding SelectedSibling}">
                    <DataGridComboBoxColumn.ElementStyle>
                        <Style TargetType="{x:Type ComboBox}">
                            <Setter Property="ItemsSource" Value="{Binding Siblings}" />
                        </Style>
                    </DataGridComboBoxColumn.ElementStyle>
                    <DataGridComboBoxColumn.EditingElementStyle>
                        <Style TargetType="{x:Type ComboBox}">
                            <Setter Property="ItemsSource" Value="{Binding Siblings}" />
                        </Style>
                    </DataGridComboBoxColumn.EditingElementStyle>
                </DataGridComboBoxColumn>
                ...
            </DataGrid.Columns>
        </DataGrid>

And now, we get the desired output: