C# 验证失败时禁用保存按钮

C# 验证失败时禁用保存按钮,c#,wpf,mvvm,C#,Wpf,Mvvm,正如你可能从标题中看到的,我将要问一些以前被问过很多次的问题。但是,在阅读了所有其他问题之后,我仍然找不到一个解决问题的好办法 我有一个带有基本验证的模型类: partial class Player : IDataErrorInfo { public bool CanSave { get; set; } public string this[string columnName] { get { string

正如你可能从标题中看到的,我将要问一些以前被问过很多次的问题。但是,在阅读了所有其他问题之后,我仍然找不到一个解决问题的好办法

我有一个带有基本验证的模型类:

partial class Player : IDataErrorInfo
{
    public bool CanSave { get; set; }

    public string this[string columnName]
    {
        get 
        { 
            string result = null;
            if (columnName == "Firstname")
            {
                if (String.IsNullOrWhiteSpace(Firstname))
                {
                    result = "Geef een voornaam in";
                }
            }
            if (columnName == "Lastname")
            {
                if (String.IsNullOrWhiteSpace(Lastname))
                {
                    result = "Geef een familienaam in";
                }
            }
            if (columnName == "Email")
            {
                try
                {
                    MailAddress email = new MailAddress(Email);
                }
                catch (FormatException)
                {
                    result = "Geef een geldig e-mailadres in";
                }
            }
            if (columnName == "Birthdate")
            {
                if (Birthdate.Value.Date >= DateTime.Now.Date)
                {
                    result = "Geef een geldige geboortedatum in";
                }
            }

            CanSave = true; // this line is wrong
            return result;
        }
    }

    public string Error { get { throw new NotImplementedException();} }
}
partial class Player : IDataErrorInfo
{
    private delegate string Validation(string value);
    private Dictionary<string, Validation> columnValidations;
    public List<string> Errors;

    public Player()
    {
        columnValidations = new Dictionary<string, Validation>();
        columnValidations["Firstname"] = delegate (string value) {
            return String.IsNullOrWhiteSpace(Firstname) ? "Geef een voornaam in" : null;
        }; // Add the others...

        errors = new List<string>();
    }

    public bool CanSave { get { return Errors.Count == 0; } }

    public string this[string columnName]
    {
        get { return this.GetProperty(columnName); } 

        set
        { 
            var error = columnValidations[columnName](value);

            if (String.IsNullOrWhiteSpace(error))
                errors.Add(error);
            else
                this.SetProperty(columnName, value);
        }
    }
}
每次属性更改时都会执行此验证(因此每次用户在文本框中键入字符时):


这很好用。验证发生(绑定的
PropertyChanged
代码在VM中的CurrentPlayer属性上完成,该属性是Player的对象)

我现在想做的是在验证失败时禁用保存按钮

首先,最简单的解决方案似乎可以在这个线程中找到:

  • 如果我想遵循公认的解决方案,我必须写我的 验证代码两次,因为我不能简单地使用索引器。书写 双重代码绝对不是我想要的,所以这不是一个解决方案 解决我的问题
  • 关于这个问题的第二个答案听起来很有希望, 但问题是我有多个字段必须 验证。这样,所有内容都依赖于上次选中的属性 (因此,如果该字段填写正确,
    CanSave
    将为真,甚至 尽管还有其他字段仍然无效)
  • 我发现的另一个解决方案是使用
    ErrorCount
    属性。但是当我在每次属性更改时(以及在每个键入的字符时)进行验证时,这也是不可能的-我如何知道何时增加/减少
    错误计数

    解决这个问题的最好办法是什么

    谢谢

    本文将单个验证移动到属性中:

    public partial class Player : IDataErrorInfo
    {
        Dictionary<string, string> _errorInfo;
    
        public Player()
        {
            _errorInfo = new Dictionary<string, string>();
        }
    
        public bool CanSave { get { return _errorInfo.Count == 0; }
    
        public string this[string columnName]
        {
            get 
            { 
                return _errorInfo.ContainsKey(columnName) ? _errorInfo[columnName] : null;
            }
        }
    
        public string FirstName
        {
            get { return _firstName;}
            set
            {
                if (String.IsNullOrWhiteSpace(value))
                    _errorInfo.AddOrUpdate("FirstName", "Geef een voornaam in");
                else
                {
                    _errorInfo.Remove("FirstName");
                    _firstName = value;
                }
            }
        }
    }
    
    公共部分类玩家:IDataErrorInfo
    {
    字典错误信息;
    公共玩家()
    {
    _errorInfo=新字典();
    }
    公共bool可以保存{get{return{u errorInfo.Count==0;}
    公共字符串此[string columnName]
    {
    得到
    { 
    返回_errorInfo.ContainsKey(columnName)?_errorInfo[columnName]:null;
    }
    }
    公共字符串名
    {
    获取{return\u firstName;}
    设置
    {
    if(String.IsNullOrWhiteSpace(value))
    _errorInfo.AddOrUpdate(“FirstName”,“geefeen voornaam in”);
    其他的
    {
    _errorInfo.Remove(“名字”);
    _firstName=值;
    }
    }
    }
    }
    

    (您必须处理Dictionary
    AddOrUpdate
    扩展方法)。这与您的错误计数想法类似。

    我已经实现了中所示的映射方法,在C中,这称为a,我使用它来进行验证:

    partial class Player : IDataErrorInfo
    {
        public bool CanSave { get; set; }
    
        public string this[string columnName]
        {
            get 
            { 
                string result = null;
                if (columnName == "Firstname")
                {
                    if (String.IsNullOrWhiteSpace(Firstname))
                    {
                        result = "Geef een voornaam in";
                    }
                }
                if (columnName == "Lastname")
                {
                    if (String.IsNullOrWhiteSpace(Lastname))
                    {
                        result = "Geef een familienaam in";
                    }
                }
                if (columnName == "Email")
                {
                    try
                    {
                        MailAddress email = new MailAddress(Email);
                    }
                    catch (FormatException)
                    {
                        result = "Geef een geldig e-mailadres in";
                    }
                }
                if (columnName == "Birthdate")
                {
                    if (Birthdate.Value.Date >= DateTime.Now.Date)
                    {
                        result = "Geef een geldige geboortedatum in";
                    }
                }
    
                CanSave = true; // this line is wrong
                return result;
            }
        }
    
        public string Error { get { throw new NotImplementedException();} }
    }
    
    partial class Player : IDataErrorInfo
    {
        private delegate string Validation(string value);
        private Dictionary<string, Validation> columnValidations;
        public List<string> Errors;
    
        public Player()
        {
            columnValidations = new Dictionary<string, Validation>();
            columnValidations["Firstname"] = delegate (string value) {
                return String.IsNullOrWhiteSpace(Firstname) ? "Geef een voornaam in" : null;
            }; // Add the others...
    
            errors = new List<string>();
        }
    
        public bool CanSave { get { return Errors.Count == 0; } }
    
        public string this[string columnName]
        {
            get { return this.GetProperty(columnName); } 
    
            set
            { 
                var error = columnValidations[columnName](value);
    
                if (String.IsNullOrWhiteSpace(error))
                    errors.Add(error);
                else
                    this.SetProperty(columnName, value);
            }
        }
    }
    
    部分职业玩家:IDataErrorInfo
    {
    私有委托字符串验证(字符串值);
    私有字典验证;
    公开列表错误;
    公共玩家()
    {
    columnValidations=新字典();
    columnValidations[“Firstname”]=委托(字符串值){
    返回字符串.IsNullOrWhiteSpace(Firstname)?“geefeen voornaam in”:null;
    };//添加其他。。。
    错误=新列表();
    }
    公共bool可以保存{get{返回错误。计数==0;}}
    公共字符串此[string columnName]
    {
    get{返回this.GetProperty(columnName);}
    设置
    { 
    var error=columnValidations[columnName](值);
    if(String.IsNullOrWhiteSpace(错误))
    错误。添加(错误);
    其他的
    this.SetProperty(columnName,value);
    }
    }
    }
    
    此方法适用于数据批注。您还可以将“IsValid”属性绑定到保存按钮以启用/禁用

    public abstract class ObservableBase : INotifyPropertyChanged, IDataErrorInfo
    {
        #region Members
        private readonly Dictionary<string, string> errors = new Dictionary<string, string>();
        #endregion
    
        #region Events
    
        /// <summary>
        /// Property Changed Event
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
    
        #endregion
    
        #region Protected Methods
    
        /// <summary>
        /// Get the string name for the property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        protected string GetPropertyName<T>(Expression<Func<T>> expression)
        {
            var memberExpression = (MemberExpression) expression.Body;
            return memberExpression.Member.Name;
        }
    
        /// <summary>
        /// Notify Property Changed (Shorted method name)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        protected virtual void Notify<T>(Expression<Func<T>> expression)
        {
            string propertyName = this.GetPropertyName(expression);
            PropertyChangedEventHandler handler = this.PropertyChanged;
            handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">The expression.</param>
        protected virtual void OnPropertyChanged<T>(Expression<Func<T>> expression)
        {
            string propertyName = this.GetPropertyName(expression);
            PropertyChangedEventHandler handler = this.PropertyChanged;
    
            handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        #endregion
    
        #region Properties
    
        /// <summary>
        /// Gets an error message indicating what is wrong with this object.
        /// </summary>
        public string Error => null;
    
        /// <summary>
        /// Returns true if ... is valid.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is valid; otherwise, <c>false</c>.
        /// </value>
        public bool IsValid => this.errors.Count == 0;
    
        #endregion
    
        #region Indexer
    
        /// <summary>
        /// Gets the <see cref="System.String"/> with the specified column name.
        /// </summary>
        /// <value>
        /// The <see cref="System.String"/>.
        /// </value>
        /// <param name="columnName">Name of the column.</param>
        /// <returns></returns>
        public string this[string columnName]
        {
            get
            {
                var validationResults = new List<ValidationResult>();
                string error = null;
    
                if (Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue(this), new ValidationContext(this) { MemberName = columnName }, validationResults))
                {
                    this.errors.Remove(columnName);
                }
                else
                {
                    error = validationResults.First().ErrorMessage;
    
                    if (this.errors.ContainsKey(columnName))
                    {
                        this.errors[columnName] = error;
                    }
                    else
                    {
                        this.errors.Add(columnName, error);
                    }
                }
    
                this.OnPropertyChanged(() => this.IsValid);
                return error;
            }
        }
    
        #endregion  
    }
    
    公共抽象类ObservableBase:INotifyPropertyChanged,IDataErrorInfo
    {
    #区域成员
    私有只读字典错误=新建字典();
    #端区
    #地区活动
    /// 
    ///属性更改事件
    /// 
    公共事件属性更改事件处理程序属性更改;
    #端区
    #区域保护方法
    /// 
    ///获取属性的字符串名称
    /// 
    /// 
    /// 
    /// 
    受保护的字符串GetPropertyName(表达式)
    {
    var memberExpression=(memberExpression)expression.Body;
    返回memberExpression.Member.Name;
    }
    /// 
    ///Notify属性已更改(短接方法名称)
    /// 
    /// 
    /// 
    受保护的虚拟void Notify(表达式)
    {
    string propertyName=this.GetPropertyName(表达式);
    PropertyChangedEventHandler处理程序=this.PropertyChanged;
    handler?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
    }
    /// 
    ///在[属性更改]时调用。
    /// 
    /// 
    ///表情。
    受保护的虚拟void OnPropertyChanged(表达式)
    {
    string propertyName=this.GetPropertyName(表达式);
    PropertyChangedEventHandler处理程序=this.PropertyChanged;
    handler?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
    }
    #端区
    #区域属性
    /// 
    ///获取一条错误消息,指示此对象的错误。
    /// 
    公共字符串错误=>null;
    /// 
    ///如果…有效,则返回true。
    /// 
    /// 
    ///如果此实例有效,则为true;否则为false。
    /// 
    public bool IsValid=>this.errors.Count==0;
    #端区
    #区域索引器
    /// 
    ///获取具有指定列名的。
    /// 
    /// 
    ///这个。
    /// 
    ///列的名称。
    /// 
    公共字符串此[string columnName]
    {
    得到
    {
    var validationResults=新列表();
    字符串错误=null;
    如果(Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue)(this),则新的ValidationContext(this){MemberName=columnName},valida