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

WPF MVVM:DataGrid存在验证错误时禁用Save按钮问题

问题:DataGrid存在验证错误时禁用Save按钮,删除错误行后按钮无法恢复

开发了一个供用户编辑后保存至数据库的DataGrid,希望当DataGrid存在验证错误(如数字字段输入字符)时禁用Save按钮。尝试过两种方案:直接绑定Validation.HasError到DataGrid无效;另一种方案在删除存在错误的行后,按钮会一直处于禁用状态无法恢复。


现有DataGrid代码

<DataGrid x:Name="dataGrid" ItemsSource="{Binding DataList}" SelectedItem="{Binding SelectedData}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">
     <DataGrid.Columns>
         <DataGridTemplateColumn Header="MustBeNumber">
             <DataGridTemplateColumn.CellTemplate>
                 <DataTemplate>
                     <TextBox Text="{Binding Path=NUMBER_FIELD, UpdateSourceTrigger=PropertyChanged}" >
                 </DataTemplate>
             </DataGridTemplateColumn.CellTemplate>
         </DataGridTemplateColumn>
     </DataGrid.Columns>
 </DataGrid>

模型类(ViewModel包含该模型的ObservableCollection)

public class Data : ObservableObject
{
    private int? _number_field;

    public int? NUMBER_FIELD { get { return _number_field; } set { _number_field = value; OnPropertyChanged("NUMBER_FIELD"); } }
}

尝试过的无效代码

<Button Content="Save" Name="btnSave" Command="{Binding SaveCommand}">
     <Button.Style>
         <Style TargetType="Button">
             <Setter Property="IsEnabled" Value="False"/>
             <Style.Triggers>
                 <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                         <Condition Binding="{Binding Path=(Validation.HasError), ElementName=dataGrid}" Value="False"/>
                      </MultiDataTrigger.Conditions>
                      <Setter Property="IsEnabled" Value="True"/>
                  </MultiDataTrigger>
             </Style.Triggers>
        </Style>
   </Button.Style>
</Button>

解决方案:通过ViewModel跟踪验证状态 + 命令启用控制

步骤1:给模型添加验证支持

实现IDataErrorInfo接口,让模型自身处理字段验证,并暴露错误状态属性:

public class Data : ObservableObject, IDataErrorInfo
{
    private int? _number_field;

    public int? NUMBER_FIELD 
    { 
        get { return _number_field; } 
        set 
        { 
            _number_field = value; 
            OnPropertyChanged(nameof(NUMBER_FIELD));
            OnPropertyChanged(nameof(HasError));
        } 
    }

    // IDataErrorInfo 实现
    public string Error => null;

    public string this[string columnName]
    {
        get
        {
            if (columnName == nameof(NUMBER_FIELD))
            {
                // 验证逻辑:若转换失败(输入非数字)或为空则返回错误信息
                if (_number_field == null)
                {
                    return "数字字段不能为空";
                }
            }
            return null;
        }
    }

    // 标识当前模型是否有验证错误
    public bool HasError => !string.IsNullOrEmpty(this[nameof(NUMBER_FIELD)]);
}

步骤2:在ViewModel中跟踪全局验证状态

ViewModel监听集合变化和模型属性变化,实时更新整体错误状态:

public class YourViewModel : ObservableObject
{
    private ObservableCollection<Data> _dataList;
    public ObservableCollection<Data> DataList
    {
        get => _dataList;
        set
        {
            if (_dataList != null)
            {
                _dataList.CollectionChanged -= DataList_CollectionChanged;
                foreach (var item in _dataList)
                {
                    item.PropertyChanged -= Item_PropertyChanged;
                }
            }

            _dataList = value;
            OnPropertyChanged(nameof(DataList));

            if (_dataList != null)
            {
                _dataList.CollectionChanged += DataList_CollectionChanged;
                foreach (var item in _dataList)
                {
                    item.PropertyChanged += Item_PropertyChanged;
                }
            }

            UpdateHasValidationErrors();
        }
    }

    private bool _hasValidationErrors;
    public bool HasValidationErrors
    {
        get => _hasValidationErrors;
        set
        {
            _hasValidationErrors = value;
            OnPropertyChanged(nameof(HasValidationErrors));
            SaveCommand.RaiseCanExecuteChanged();
        }
    }

    public ICommand SaveCommand { get; }

    public YourViewModel()
    {
        SaveCommand = new RelayCommand(Save, () => !HasValidationErrors);
        DataList = new ObservableCollection<Data>();
    }

    private void Save()
    {
        // 数据库保存逻辑
    }

    private void DataList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Data item in e.NewItems)
            {
                item.PropertyChanged += Item_PropertyChanged;
            }
        }
        if (e.OldItems != null)
        {
            foreach (Data item in e.OldItems)
            {
                item.PropertyChanged -= Item_PropertyChanged;
            }
        }
        UpdateHasValidationErrors();
    }

    private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Data.HasError))
        {
            UpdateHasValidationErrors();
        }
    }

    private void UpdateHasValidationErrors()
    {
        HasValidationErrors = DataList.Any(item => item.HasError);
    }
}

步骤3:修改绑定,启用验证并通过命令控制按钮

更新TextBox绑定以启用数据验证,按钮直接通过命令的CanExecute控制启用状态:

<!-- 更新DataGrid列的TextBox绑定 -->
<TextBox Text="{Binding Path=NUMBER_FIELD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

<!-- 简化按钮代码 -->
<Button Content="Save" Command="{Binding SaveCommand}" />

内容的提问来源于stack exchange,提问作者ozturk.brk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 13:36:29