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
相关产品推荐
相关产品推荐

