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

如何在WPF中获取DataGrid控件的用户输入错误并点击按钮触发提示

问题原因
  • DataGrid默认开启UI虚拟化,仅可视区域内的单元格会被渲染到视觉树中,非可视区域的单元格无法通过遍历VisualChildren获取,导致漏检错误。
  • 点击按钮时如果单元格仍处于编辑模式,绑定未完成更新,校验错误还未挂载到对应视觉元素上。
  • 校验错误的附加属性通常挂载在单元格内部的TextBox元素上,而非DataGridCell本身,直接读取DataGridCell的HasError属性会返回false。
推荐解决方案:数据源层面校验(MVVM友好,无漏检)

你已经实现了INotifyDataErrorInfo接口,直接遍历数据源集合校验即可,完全不依赖视觉树,性能和准确率都远高于遍历视觉元素。

  1. 首先补充Person类的INotifyDataErrorInfo实现示例:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Get_User_input_Errors_From_DataGrid
{
    public class Person : INotifyPropertyChanged, INotifyDataErrorInfo
    {
        private string _name;
        private int _age;
        private int _weight;
        private readonly Dictionary<string, List<string>> _errors = new();

        public string Name
        {
            get => _name;
            set
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
                ValidateName();
            }
        }

        public int Age
        {
            get => _age;
            set
            {
                _age = value;
                OnPropertyChanged(nameof(Age));
                ValidateAge();
            }
        }

        public int Weight
        {
            get => _weight;
            set
            {
                _weight = value;
                OnPropertyChanged(nameof(Weight));
                ValidateWeight();
            }
        }

        public bool HasErrors => _errors.Any();

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
        public event PropertyChangedEventHandler PropertyChanged;

        private void ValidateName()
        {
            if (string.IsNullOrWhiteSpace(Name))
                AddError(nameof(Name), "姓名不能为空");
            else
                RemoveError(nameof(Name));
        }

        private void ValidateAge()
        {
            if (Age < 0 || Age > 150)
                AddError(nameof(Age), "年龄需在0-150之间");
            else
                RemoveError(nameof(Age));
        }

        private void ValidateWeight()
        {
            if (Weight < 0 || Weight > 300)
                AddError(nameof(Weight), "体重需在0-300之间");
            else
                RemoveError(nameof(Weight));
        }

        private void AddError(string propertyName, string error)
        {
            if (!_errors.ContainsKey(propertyName))
                _errors[propertyName] = new List<string>();
            if (!_errors[propertyName].Contains(error))
            {
                _errors[propertyName].Add(error);
                ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        private void RemoveError(string propertyName)
        {
            if (_errors.Remove(propertyName))
                ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
        }

        public IEnumerable GetErrors(string propertyName)
        {
            return string.IsNullOrEmpty(propertyName) || !_errors.ContainsKey(propertyName)
                ? Enumerable.Empty<string>()
                : _errors[propertyName];
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  1. 修改按钮点击逻辑,直接遍历数据源收集错误:
private void Button_Click(object sender, RoutedEventArgs e)
{
    var vm = DataContext as ViewModel;
    var errorList = new List<string>();
    for (int i = 0; i < vm.People.Count; i++)
    {
        var person = vm.People[i];
        if (person.HasErrors)
        {
            var ageErrors = person.GetErrors(nameof(Person.Age))?.Cast<string>().ToList();
            if (ageErrors != null && ageErrors.Any())
                errorList.Add($"第{i+1}行年龄错误:{string.Join(",", ageErrors)}");
            var nameErrors = person.GetErrors(nameof(Person.Name))?.Cast<string>().ToList();
            if (nameErrors != null && nameErrors.Any())
                errorList.Add($"第{i+1}行姓名错误:{string.Join(",", nameErrors)}");
            var weightErrors = person.GetErrors(nameof(Person.Weight))?.Cast<string>().ToList();
            if (weightErrors != null && weightErrors.Any())
                errorList.Add($"第{i+1}行体重错误:{string.Join(",", weightErrors)}");
        }
    }
    if (errorList.Any())
    {
        MessageBox.Show($"检测到以下错误:\n{string.Join("\n", errorList)}");
    }
    else
    {
        MessageBox.Show("数据校验通过");
    }
}
可选方案:修复视觉树遍历逻辑

如果确实需要从视觉层获取错误,修改如下:

  1. 先给DataGrid命名,方便调用:
<DataGrid x:Name="MyDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding Path=People,Mode=TwoWay}">
  1. 修改按钮点击逻辑,先提交编辑,再校验单元格内部元素:
private void Button_Click(object sender, RoutedEventArgs e)
{
    // 先提交所有未保存的编辑
    MyDataGrid.CommitEdit(DataGridEditingUnit.Cell, true);
    MyDataGrid.CommitEdit(DataGridEditingUnit.Row, true);

    var errorList = new List<string>();
    var allCells = Helpers.FindVisualChildren<DataGridCell>(MyDataGrid).ToList();
    foreach (var cell in allCells)
    {
        // 检查单元格自身和内部所有子元素的校验错误
        var elementsWithError = Helpers.FindVisualChildren<DependencyObject>(cell)
            .Where(Validation.GetHasError).ToList();
        if (elementsWithError.Any())
        {
            var cellContent = cell.Content is TextBlock tb ? tb.Text : cell.Content.ToString();
            errorList.Add($"单元格错误:{cellContent}");
        }
    }
    if (errorList.Any())
    {
        MessageBox.Show($"检测到以下错误:\n{string.Join("\n", errorList)}");
    }
}

注意:该方案仍受UI虚拟化影响,非可视区域的单元格错误无法检测,仅适合小数据量场景使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 08:06:04