You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF DataGrid数值单元格格式异常如何触发IDataErrorInfo验证?

解决WPF DataGrid数值单元格格式异常跳过IDataErrorInfo验证的问题

问题核心在于:WPF绑定数值类型属性时,若用户输入非法格式(如文本),会先触发转换异常,绑定引擎此时不会更新源属性,导致IDataErrorInfo的this[string propertyName]方法无法触发,只能显示WPF默认的格式错误提示。

以下是几种可行的解决方法:


方法1:使用可空数值类型接收无效输入

将ViewModel中数值类型的属性改为可空值类型(如int?、decimal?),这样当用户输入非法文本时,绑定会自动将属性值设为null,此时就能触发IDataErrorInfo的验证逻辑,返回自定义错误信息。

示例代码

Product类(ViewModel)

public class Product : IDataErrorInfo, INotifyPropertyChanged
{
    private decimal? _price;
    public decimal? Price
    {
        get => _price;
        set
        {
            _price = value;
            OnPropertyChanged(nameof(Price));
        }
    }

    private int? _stock;
    public int? Stock
    {
        get => _stock;
        set
        {
            _stock = value;
            OnPropertyChanged(nameof(Stock));
        }
    }

    // IDataErrorInfo实现
    public string this[string propertyName]
    {
        get
        {
            switch (propertyName)
            {
                case nameof(Price):
                    if (!Price.HasValue)
                        return "请输入有效的数字价格";
                    if (Price.Value < 0)
                        return "价格不能为负数";
                    break;
                case nameof(Stock):
                    if (!Stock.HasValue)
                        return "请输入有效的整数库存";
                    if (Stock.Value < 0)
                        return "库存不能为负数";
                    break;
            }
            return null;
        }
    }

    public string Error => null;

    // INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML绑定设置

<DataGridTextColumn 
    Header="价格" 
    Binding="{Binding Price, TargetNullValue='', ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
<DataGridTextColumn 
    Header="库存" 
    Binding="{Binding Stock, TargetNullValue='', ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />

TargetNullValue=''用于将空输入或无效输入映射为null,确保属性能接收到值并触发验证。


方法2:自定义转换器捕获格式异常

若无法修改属性类型,可通过自定义值转换器捕获转换过程中的格式异常,将错误信息传递给ViewModel,再由IDataErrorInfo返回。

示例代码

自定义数值转换器

public class NumericConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value?.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var input = value?.ToString()?.Trim();
        if (string.IsNullOrEmpty(input))
            return Activator.CreateInstance(targetType);

        try
        {
            return Convert.ChangeType(input, targetType, culture);
        }
        catch (FormatException)
        {
            // 将错误信息传递给ViewModel
            if (parameter is Product product)
            {
                product.SetValidationError(parameter.ToString(), "输入格式无效,请输入数字");
            }
            return Activator.CreateInstance(targetType);
        }
    }
}

Product类扩展

public class Product : IDataErrorInfo, INotifyPropertyChanged
{
    private decimal _price;
    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            OnPropertyChanged(nameof(Price));
            // 清除之前的格式错误
            ClearValidationError(nameof(Price));
        }
    }

    // 存储临时验证错误
    private readonly Dictionary<string, string> _validationErrors = new();

    public void SetValidationError(string propertyName, string error)
    {
        if (_validationErrors.ContainsKey(propertyName))
            _validationErrors[propertyName] = error;
        else
            _validationErrors.Add(propertyName, error);
        OnPropertyChanged(propertyName);
    }

    public void ClearValidationError(string propertyName)
    {
        _validationErrors.Remove(propertyName);
        OnPropertyChanged(propertyName);
    }

    // IDataErrorInfo实现
    public string this[string propertyName]
    {
        get
        {
            if (_validationErrors.TryGetValue(propertyName, out var error))
                return error;

            // 自定义业务验证
            if (propertyName == nameof(Price) && Price < 0)
                return "价格不能为负数";

            return null;
        }
    }

    public string Error => null;

    // INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML引用转换器

<Window.Resources>
    <local:NumericConverter x:Key="NumericConverter" />
</Window.Resources>

<DataGridTextColumn 
    Header="价格" 
    Binding="{Binding Price, Converter={StaticResource NumericConverter}, ConverterParameter={Binding}, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />

方法3:调整绑定验证步骤顺序

通过修改绑定的ValidationStep,让绑定先尝试更新源属性,再触发IDataErrorInfo验证。此方法需配合ExceptionValidationRule捕获转换异常。

XAML绑定设置

<DataGridTextColumn Header="价格">
    <DataGridTextColumn.Binding>
        <Binding Path="Price" ValidatesOnDataErrors="True" NotifyOnValidationError="True">
            <Binding.ValidationRules>
                <ExceptionValidationRule ValidationStep="UpdatedValue" />
            </Binding.ValidationRules>
        </Binding>
    </DataGridTextColumn.Binding>
</DataGridTextColumn>

ViewModel属性处理

private decimal _price;
public decimal Price
{
    get => _price;
    set
    {
        try
        {
            _price = value;
            OnPropertyChanged(nameof(Price));
            ClearValidationError(nameof(Price));
        }
        catch (Exception ex)
        {
            SetValidationError(nameof(Price), "输入格式无效");
        }
    }
}

推荐方案

优先选择方法1,实现简单且符合WPF绑定的原生逻辑,无需额外编写转换器或复杂的验证规则。

内容的提问来源于stack exchange,提问作者YarH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:59:52