Windows Desktop Development

How to handle exception in WPF DataGrid CellEditTemplate?
Posted: 2 days ago by wpf_guru
UG
wpf_guru
2 days ago

Hello everyone,

I'm encountering an issue with validating user input in a WPF DataGrid. I have a DataGridTextColumn with a CellEditTemplate that uses a TextBox. I want to catch any exceptions that occur when the user enters invalid data (e.g., non-numeric input when expecting a number) and display an error message without losing focus or crashing the application.

Currently, when an invalid value is entered and the cell loses focus, the default WPF behavior is to revert the change or show a red border. I need more control over this. Specifically, I'd like to use a ValidationRule but also handle potential exceptions within the CellEditTemplate itself if the ValidationRule isn't sufficient.

Here's a simplified example of my setup:

<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding MyItems}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Value" Binding="{Binding NumericValue, ValidatesOnDataErrors=True, NotifyOnValidationError=True}">
            <DataGridTextColumn.CellEditTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding NumericValue, UpdateSourceTrigger=LostFocus}" />
                </DataTemplate>
            </DataGridTextColumn.CellEditTemplate>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

And in my ViewModel:

public class MyItem : INotifyPropertyChanged, IDataErrorInfo
{
    private double _numericValue;
    public double NumericValue
    {
        get { return _numericValue; }
        set
        {
            if (_numericValue != value)
            {
                _numericValue = value;
                OnPropertyChanged(nameof(NumericValue));
            }
        }
    }

    // Implementation for IDataErrorInfo for basic validation
    public string Error => null;
    public string this[string columnName]
    {
        get
        {
            if (columnName == "NumericValue")
            {
                if (_numericValue < 0) return "Value cannot be negative.";
                // How to catch exceptions here if parsing fails?
            }
            return null;
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Any advice on how to implement robust exception handling during cell editing would be greatly appreciated!

AJ
aj_dev
1 day ago

Hi wpf_guru,

You're on the right track with ValidatesOnDataErrors and NotifyOnValidationError. For handling exceptions during parsing, especially when the binding is to a non-string type like double, you can leverage the built-in converter logic or create a custom one.

The most straightforward way to handle this within the binding itself is to use a BindingConverter that includes error handling.

Consider modifying your binding like this:

<DataGridTextColumn Header="Value" Binding="{Binding NumericValue, UpdateSourceTrigger=LostFocus, Converter={StaticResource DoubleConverter}, ValidatesOnDataErrors=True, NotifyOnValidationError=True}">
    <DataGridTextColumn.Resources>
        <local:DoubleConverter x:Key="DoubleConverter" />
    </DataGridTextColumn.Resources>
</DataGridTextColumn>

And here's a sample DoubleConverter implementation:

using System;
using System.Globalization;
using System.Windows.Data;

public class DoubleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double d)
        {
            return d.ToString();
        }
        return value; // Or handle null/other types
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is string s)
        {
            if (double.TryParse(s, NumberStyles.Any, culture, out double result))
            {
                return result;
            }
            else
            {
                // Throwing an exception here can be caught by the binding system
                // or you can return Binding.DoNothing or a specific error value
                throw new FormatException($"Invalid input: '{s}' is not a valid number.");
            }
        }
        return Binding.DoNothing; // For null or unexpected types
    }
}

When double.TryParse fails in ConvertBack, throwing a FormatException will trigger the binding system's validation error mechanism. WPF will then use the ValidatesOnDataErrors and NotifyOnValidationError settings to display the error.

If you want to explicitly catch and display errors differently, you can also handle the Validation.Error event on the TextBox within the CellEditTemplate.

LW
lw_coder
18 hours ago

Thanks, aj_dev! The converter approach is clean. I also found that setting ValidatesOnNotifyDataErrors=True (which is available in newer .NET versions) can be helpful for data models implementing INotifyDataErrorInfo. The key is that the binding system intercepts the exceptions thrown from IValueConverter.ConvertBack or IDataErrorInfo.

For more granular control, you could wrap the TextBox in the CellEditTemplate with a Validation.ErrorTemplate.

<DataGridTextColumn Header="Value" Binding="{Binding NumericValue, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True}">
    <DataGridTextColumn.CellEditTemplate>
        <DataTemplate>
            <TextBox Text="{Binding NumericValue, UpdateSourceTrigger=LostFocus}">
                <Validation.ErrorTemplate>
                    <ControlTemplate>
                        <StackPanel Orientation="Horizontal">
                            <AdornedElementPlaceholder />
                            <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red" Margin="5,0,0,0"/>
                        </StackPanel>
                    </ControlTemplate>
                </Validation.ErrorTemplate>
            </TextBox>
        </DataTemplate>
    </DataGridTextColumn.CellEditTemplate>
</DataGridTextColumn>

This allows you to define exactly how validation errors are visually presented, for instance, by showing a red tooltip or an inline message.

Leave a Reply