Search code examples
c#wpfxamldatagridtextcolumn

Why is DataGridTextColumn's bound ObservableCollection is not updated?


In my XAML I have definde a DataGrid containing DataGridTextXolumns. All of the simple columns updates the source (ObservableCollection) well, but not the source field which is a List<string> field. What am I missing to define in the XAML?

<UserControl x:Class="MyTool.MyTabItem"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ScrollViewer HorizontalScrollBarVisibility="Auto">
            <DataGrid x:Name="notificationsGrid" ItemsSource="{Binding}" 
                  AlternatingRowBackground="LightBlue" AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False" 
                  SelectionMode="Single" BorderThickness="3" Unloaded="DataGrid_Unloaded" >
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Op" Binding="{Binding Mode=TwoWay, Path=Notification.Op}">
                    <DataGridTextColumn Header="DetailParams" Binding="{Binding Path=Notification.DetailParams, Mode=TwoWay, 
                                                                    Converter={StaticResource StringListConverter}, UpdateSourceTrigger=LostFocus}"> 
                    </DataGridTextColumn>
                </DataGrid.Columns>
            </DataGrid>
        </ScrollViewer>
    </Grid>
</UserControl>

Updating of column 'Op' works fine but 'DetailParams' not.

Here's the code of the used StringToListConverter:

    public class StringToListConverter : IValueConverter
{
    /// <summary>
    /// Converts a comma separated string into a List<string>.
    /// </summary>
    /// <param name="value"></param>
    /// <param name="targetType"></param>
    /// <param name="parameter"></param>
    /// <param name="culture"></param>
    /// <returns></returns>
    object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if( value is string)
        {
            return value;
        }            

        Console. WriteLine("DetailParams: " + string.Join(",", ((List<string>)value)));
        return string.Join(",", (List<string>)value);
    }

    /// <summary>
    /// Converts a List<string> into a comma separated string.
    /// </summary>
    /// <param name="value"></param>
    /// <param name="targetType"></param>
    /// <param name="parameter"></param>
    /// <param name="culture"></param>
    /// <returns></returns>
    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Console.WriteLine(string.Join(",", value));
        return string.Join(",", value);
    }
}

The DataSource of the UserControl is defined as follows:

    public partial class RawNotificationTabItem : UserControl//, INotifyPropertyChanged
{
    private ObservableCollection<RawNotificationRowViewModel> _notifications { get; set; }
    //public StringToBoolConverter StringToBoolConverter {
    //    get { return StringToBoolConverter; }
    //    set { StringToBoolConverter = value; }
    //}

    public RawNotificationTabItem(ObservableCollection<RawNotificationRowViewModel> rnvm)
    {
        _notifications = rnvm == null ? _notifications = new ObservableCollection<RawNotificationRowViewModel>() : rnvm;
        this._notifications = rnvm;
        DataContext = _notifications;
        InitializeComponent();
    }
    ...
}

Solution

  • When Notification.DetailParams is of type List<string> and you want to convert it to a single string for display and editing, the converter should look like this:

    public class StringListToStringConverter : IValueConverter
    {
        public object Convert(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            return string.Join(",", (IEnumerable<string>)value);
        }
    
        public object ConvertBack(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            return new List<string>(((string)value).Split(','));
        }
    }
    

    If you change the property type to IEnumerable<string>, you can simplify the ConvertBack method to directly return the string[] returned by String.Split:

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((string)value).Split(',');
    }