matti matti - 2 months ago 31
C# Question

Why changing DataGrid ComboBox value does not update the bound property at all?

I have DataGridComboBoxColumn that is supposed to show integers or text "Default". When I add row the combobox gets correct value from viewmodel's bound property, but when I change value in user interface, the property's set is not called. I tried both SelectedValueBinding and SelectedItemBinding. Converter's ConvertBack is never called. I don't event know should it be called.

Things that work:


  • DataGrid SelectedItem binding

  • Text column binding both ways (omitted here for shortness)



Here is my code:

XAML:

<DataGrid Name="SelectionSetsGrid" CanUserAddRows="False" CanUserResizeColumns="True" CanUserSortColumns="True"
ItemsSource="{Binding SelectionSets}" AutoGenerateColumns="False"
SelectedItem="{Binding SelectedSelectionSet}">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Width" SelectedValueBinding="{Binding LineWidthIndex}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>


ViewModel (ViewModel implements INotifyPropertyChanged and SetValue raises PropertyChanged):

public class SelectedObjectsViewModel : ViewModel
{
private int[] _lineWidths = { -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
public ObservableCollection<int> LineWidths { get; private set; };

private ObservableCollection<SelectionSetViewModel> _selectionSets;
public ObservableCollection<SelectionSetViewModel> SelectionSets
{
get { return _selectionSets; }
set { this.SetValue(ref _selectionSets, value); }
}

private SelectionSetViewModel _selectedSelectionSet;
public SelectionSetViewModel SelectedSelectionSet
{
get { return this._selectedSelectionSet; }
set { this.SetValue(ref _selectedSelectionSet, value); }
}
}


ViewModel for DataGrid row (ViewModel implements INotifyPropertyChanged and SetValue raises PropertyChanged):

public class SelectionSetViewModel : ViewModel
{
public SelectionSetViewModel()
{
LineWidthIndex = -1;
}
private int _lineWidthIndex;
public int LineWidthIndex
{
get { return _lineWidthIndex; }
set { SetValue(ref _lineWidthIndex, value); }
}


Converter:

public class IntToIntTextOrDefaultConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if ((int)value == -1)
return Fusion.App.Current.Resources["StrDefault"].ToString();

return value.ToString();
}

public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}

Answer

It seems that on some occasions like after editing the text column and pressing enter or adding new row the property WAS actually updated (set called) after changing combobox value. So I just added UpdateSourceTrigger=PropertyChanged to binding and the update to source property happened immediately (and not after some random operation). Note that changing focus from ComboBox was not enough to update source property so I thought it was never updated.

     <DataGrid Name="SelectionSetsGrid" CanUserAddRows="False" CanUserResizeColumns="True" CanUserSortColumns="True" 
                      ItemsSource="{Binding SelectionSets}" AutoGenerateColumns="False"
                      SelectedItem="{Binding SelectedSelectionSet}">
                <DataGridComboBoxColumn Header="{StaticResource XpStrTopologyWidth}" SelectedItemBinding="{Binding LineWidthIndex, UpdateSourceTrigger=PropertyChanged}">
                    <DataGridComboBoxColumn.ElementStyle>
                        <Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
                            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
                            <Setter Property="IsReadOnly" Value="True"/>
                            <Setter Property="ItemTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <WrapPanel>
                                            <TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
                                        </WrapPanel>
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </DataGridComboBoxColumn.ElementStyle>
                    <DataGridComboBoxColumn.EditingElementStyle>
                        <Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
                            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
                            <Setter Property="ItemTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <WrapPanel>
                                            <TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
                                        </WrapPanel>
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </DataGridComboBoxColumn.EditingElementStyle>
                </DataGridComboBoxColumn>                        
            </DataGrid.Columns>
        </DataGrid>