Disable save button when verification is completed

As you can see from the title, I'm going to ask what was asked many times before. But still, after reading all these other questions, I can’t find a decent solution to my problem.

I have a model class with basic validation:

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();} } } 

This check is performed every time a property changes (therefore, every time a user types a character in a text field):

 <TextBox Text="{Binding CurrentPlayer.Firstname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="137" IsEnabled="{Binding Editing}" Grid.Row="1"/> 

This works great. Verification is in progress ( PropertyChanged code for the binding is executed in the VM on the CurrentPlayer property, which is the Player object).

Now I would like to disable the save button if the validation fails.

First of all, the easiest solutions seem to be found in this thread:
Enable Disable save button during validation using IDataErrorInfo

  • If I want to follow the decision I have to write twice, because I can’t just use the index. Writing a double code is absolutely not what I want, so it is not a solution to my problem.
  • The second answer on this topic sounded very promising, but the problem is that I have several fields that need to be confirmed. Thus, it all depends on the last property checked (therefore, if this field is filled correctly, CanSave will be true, even though there are other fields that are still not valid).

Another solution I found is using the ErrorCount property. But since I check with every change in the property (and so on for every typed character), this is also impossible - how can I find out when to increase / decrease the ErrorCount value?

What would be the best way to solve this problem?

thanks

+3
source share
3 answers

I implemented the map approach shown in my comment above , in C # this is called a Dictionary , in which I use anonymous methods to check:

 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); } } } 
+1
source

This article http://www.asp.net/mvc/tutorials/older-versions/models-%28data%29/validating-with-the-idataerrorinfo-interface-cs moves an individual check into properties:

 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; } } } } 

(you will have to handle the Dictionary AddOrUpdate extension method). This is similar to the idea of ​​counting errors.

+3
source

This approach works with data annotations. You can also bind the "IsValid" property to the "Save" button to enable / disable.

 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 } 
0
source

Source: https://habr.com/ru/post/1446175/


All Articles