Search code examples
c#wpfdata-bindingdatagriddatagridcomboboxcolumn

How to make the ComboBox column's editor elements save the user-selected new value?


The full XAML and code-behind are below. I have a Window with a DataGrid with a single column, a DataGridComboBoxColumn. In its EditingElementStyle I set the ComboBox-es editable and set their ItemsSource to the same collection to which I bind in the ElementStyle property. The bindings seem to work well until I run the program and try to change the value of a cell to another value. Although I currently select only from existing values (in future I want to allow entering custom items) the value is not saved and the previous value is shown after moving the focus.

If I put just this in the Window:

<StackPanel Orientation="Vertical">
    <ComboBox ItemsSource="{Binding Path=MyGroups,
                            RelativeSource={RelativeSource Mode=FindAncestor,
                            AncestorType={x:Type Window}}}" IsEditable="True"/>
    <ComboBox ItemsSource="{Binding Path=MyGroups,
                            RelativeSource={RelativeSource Mode=FindAncestor,
                            AncestorType={x:Type Window}}}" IsEditable="True"/>
</StackPanel>

everything seems to be working.

XAML

<Window x:Class="cs_wpf_test_11.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:cs_wpf_test_11"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="TestWindow" Height="450" Width="800">
    <Grid>
        <DataGrid Name="MyDataGrid" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridComboBoxColumn Header="My only column"
                                        SelectedItemBinding="{Binding Value, Mode=TwoWay}"
                                        TextBinding="{Binding Value}">
                    <DataGridComboBoxColumn.ElementStyle>
                        <Style TargetType="ComboBox">
                            <Setter Property="ItemsSource" Value="{Binding Path=MyGroups,
                                RelativeSource={RelativeSource Mode=FindAncestor,
                                AncestorType={x:Type Window}}}"/>
                        </Style>
                    </DataGridComboBoxColumn.ElementStyle>
                    <DataGridComboBoxColumn.EditingElementStyle>
                        <Style TargetType="ComboBox">
                            <Setter Property="IsEditable" Value="True"/>
                            <Setter Property="ItemsSource" Value="{Binding Path=MyGroups,
                                RelativeSource={RelativeSource Mode=FindAncestor,
                                AncestorType={x:Type Window}}}"/>
                        </Style>
                    </DataGridComboBoxColumn.EditingElementStyle>
                </DataGridComboBoxColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Code-behind

public partial class TestWindow : Window
{
    public ObservableCollection<ItemType> MyItems { get; set; }
    public ObservableCollection<string> MyGroups { get; set; }
    public TestWindow()
    {
        InitializeComponent();
        MyGroups = new ObservableCollection<string>();
        MyGroups.Add("test");
        MyGroups.Add("test 2");
        MyItems = new ObservableCollection<ItemType>();
        MyItems.Add(new ItemType()
        {
            Value = "test"
        });
        MyItems.Add(new ItemType()
        {
            Value = "test 2"
        });
        MyDataGrid.ItemsSource = MyItems;
    }
}

public class ItemType : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    protected string _Value = "";
    public string Value
    {
        get
        {
            return _Value;
        }
        set
        {
            if (_Value != value)
            {
                _Value = value;
                OnPropertyChanged("Value");
            }
        }
    }
}

Expected:

  1. The user selects a cell,
  2. enters in its edit mode (through double click or F2) and
  3. selects from the drop-down another value than the one previously shown.
  4. The user changes the selected cell to another
  5. and the previously focused cell remains with the newly selected value.

Actual: the user does the same steps as above, but when focusing another cell after selecting a new value, the previously focused cell remains with the initial value.


Solution

  • It seems that the TextBinding is already with the Mode=TwoWay. I just removed this line from the markup and everything works as expected:

    SelectedItemBinding="{Binding Value, Mode=TwoWay}"