I had such a problem: I need the button to become inactive if there is an error in the TextBox. The problem is that there are several such TextBoxes. I tried to do the usual validation on exceptions and a few flags for each of the fields denoting their validity and to bind a button. But it is very dreary. And if you add fields? All new flags to create? Terribly uncomfortable and clumsy. Please tell me some more beautiful way.

  • Look at this question. - Vlad
  • @Vlad I perfectly understand the validation mechanisms. I do not need these obvious things to poke. And specifically that topic in particular, I have already seen before. She does not solve my problem. - PECHAPTER
  • Then it is not clear what the problem is? You put all the properties you need to validate in the VM, hang the corresponding attributes and bind them to the TextBox. The availability of the command is associated with the presence of validation errors. When adding new fields, no additional gestures will be required. - Vlad
  • @Vlad button problem. Exclusively in it. It must be inactive if there are input errors in the TextBox. - PECHAIR
  • So bind the CanExecute command, which is tied to the button, to the presence of validation errors. - Vlad

2 answers 2

If you use MVVM, then everything is quite simple. Doing validation at the data source. Properties with validation errors are listed. If the list is empty, then there are no errors, which means you can click on the button. Here is an example of a VM:

public sealed class MainVm : INotifyPropertyChanged, IDataErrorInfo { // ΠΏΠ΅Ρ€Π΅Ρ‡Π΅Π½ΡŒ свойств с ошибками private readonly HashSet<string> propertiesWithErrors = new HashSet<string>(); // ΠΌΠ΅Ρ‚ΠΎΠ΄ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ свойства private string Validate(string propertyName) { var value = GetType() .GetProperty(propertyName) .GetValue(this, null); var results = new List<ValidationResult>(); var context = new ValidationContext(this, null, null) { MemberName = propertyName }; if (!Validator.TryValidateProperty(value, context, results)) { // добавляСм ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ это свойство содСрТит ΠΎΡˆΠΈΠ±ΠΊΡƒ propertiesWithErrors.Add(propertyName); return results.First().ErrorMessage; } // удаляСм ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎΠ± ошибкС Π² свойствС propertiesWithErrors.Remove(propertyName); return string.Empty; } // фамилия (бросаСт PropertyChanged) [Required(AllowEmptyStrings = false)] public string Surname { get; set; } // имя (бросаСт PropertyChanged) [Required(AllowEmptyStrings = false)] public string Name { get; set; } // ΠΊΠΎΠΌΠ°Π½Π΄Π°, которая привязывСтся ΠΊ ΠΊΠ½ΠΎΠΏΠΊΠ΅. Допустим, ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊΠΈΠ΅-Π»ΠΈΠ±ΠΎ измСнСния public ICommand ApplyCommand { get; } // рСализация IDataErrorInfo public string Error { get { throw new NotImplementedException(); } } string IDataErrorInfo.this[string propertyName] { get { return Validate(propertyName); } } // ------ public MainVm() { // создаСм ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ // ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ Execute (Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ Π΄Π΅Π»Π°Π΅ΠΌ) // Π²Ρ‚ΠΎΡ€ΠΎΠΉ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ CanExecute (true, Ссли Π½Π΅Ρ‚ свойств с ошибками) ApplyCommand = new RelayCommand(o => { }, o => !propertiesWithErrors.Any()); } 

RelayCommand can be pulled from here . The presentation is still easier. Bind properties to the TextBox, and the button to the command.

 <Window ...> <Window.DataContext> <vm:MainVm/> </Window.DataContext> <StackPanel> <TextBox Text="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/> <TextBox Text="{Binding Surname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/> <Button Content="Button" Command="{Binding ApplyCommand}"/> </StackPanel> </Window> 

UPD

No, the Required attribute has nothing to do with INotifyPropertyChanged. The property, in fact, should look like this:

 public string Name { get { return name; } set { if (name != value) { name = value; OnPropertyChanged(nameof(Name)); } } } private string name; private void OnPropertyChanged(string propertyName) { var tmp = PropertyChanged; if (tmp != null) { tmp(this, new PropertyChangedEventArgs(propertyName)); } } 

The Required attribute says that this property should be set to a value other than null and string.Empty.

  • What is the attribute Required? - PECHAPTER
  • From System.ComponentModel.DataAnnotations - Vlad
  • I did not understand, is it causing PropertyChanged itself ??? Those. is it like Fody.PropertyChanged analog from .NET? - PECHAPTER
  • @DarkByte, what is it? - Vlad
  • Well, what does this attribute itself call PropertyChanged when a property changes? - PECHAPTER

You can add a Count field for the button, if the textbox changed its status to wrong, then +1 to count, if it changed from wrong to correct, then -1 count. (Here we must carefully make the field add \ cancel only when the status is changed to the opposite, that is, the count value interval is from 0 to the textbox number.) Accordingly, as soon as count turned to 0, the button works.

  • here I already thought of it myself. Well, if no one offers a more elegant solution, I will probably do so. - PECHAPTER
  • can be done with events - R0manych
  • in terms of events? - PECHAIRTER
  • There is still a problem in that it is always necessary to declare a binding to the text field. Because it is necessary to increment the counter even in case of incorrect conversion. Horror, of course, how uncomfortable anyway ... - PECHAPPER
  • For example, if I have a double field, but I still have to declare a textual for binding, in order to check the conversion to double in the setter. This, too, is a mistake ... - PECHAPTER