Задача: создать аккуратную систему валидации контролов в Window WPF, причем на основе их взаимосвязи. Например, если стоит RadioButton "пол - женский" - то валидируем группу TextBox'ов по их значению, а если не стоит - не валидируем. Просто отключать группу TextBox'ов нельзя по бизнес-логике. Пользователь должен иметь возможность вводить любые данные в любой момент, но нужно красить красным то, что вводить не нужно, а не запрещать вводить.
Создал во VM блок валидации на основе IDataErrorInfo:
public string this[string columnName]
{
get
{
string error = String.Empty;
switch (columnName)
{
case "RelationOther":
if (!string.IsNullOrEmpty(RelationOther) && !RelationTypeMap[10])
error = "Ошибка! Указание описания не требуется"; break;
case "Position":
//Обработка ошибок для свойства Position
break;
}
return error;
}
}
public string Error
{
get { return string.Empty; }
}
где RelationTypeMap[10] - это элемент, соответствующий варианту "не родственник" в массиве, на который привязан набор из 11 RadioButton'ов. Смысл: валидировать RelationOther только есть отмечен пункт иной пункт, кроме "не родственник", в противном случае - не валидировать и молча принимать введенную строку.
Вот разметка:
<TextBox Name="RelationOther" Style="{StaticResource TextBox}"
Text="{Binding RelationOther, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
<RadioButton Grid.Column="2" Grid.Row="3" GroupName="q1" Style="{StaticResource LeftChecker}" IsChecked="{Binding RelationTypeMap[10], Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<RadioButton.Content>
<TextBlock Text="не род- ственник" Style="{StaticResource CheckerText}"/>
</RadioButton.Content>
</RadioButton>
Проблема: если RadioButton уже стоит в позиции, кроме "не родственник" - все валидируется. Если уже стоит в позиции "не родственник" - не валидируется, как и нужно. Однако, если написать текст, и сменить галочку - то событие валидации не срабатывает. Смена галочки вообще не вызывает это событие, отладчик даже не попадает в индексатор public string this[string columnName].
Подскажите, пожалуйста, как мне заставить VM запускать валидацию текстового поля, если меняется связанные с ним логически RadioButton'ы?
Например, можно сделать Boolean свойства для каждой Radiobutton и общее итоговое свойство, значение которого изменяется в зависимости от выбранной радиокнопки, вот такая вьюмодель
public class MainViewModel : INotifyPropertyChanged, IDataErrorInfo
{
//ctor
public MainViewModel()
{
AnimalType = "NotAnimal";
}
public bool NotAnimalSelected
{
get => AnimalType.Equals("NotAnimal");
set => AnimalType = "NotAnimal";
}
public bool CowSelected
{
get => AnimalType.Equals("Cow");
set => AnimalType = "Cow";
}
public bool DogSelected
{
get => AnimalType.Equals("Dog");
set => AnimalType = "Dog";
}
public bool CatSelected
{
get => AnimalType.Equals("Cat");
set => AnimalType = "Cat";
}
private string _AnimalType;
public string AnimalType
{
get => _AnimalType;
set
{
_AnimalType = value;
OnPropertyChanged("NotAnimalSelected");
OnPropertyChanged("CowSelected");
OnPropertyChanged("DogSelected");
OnPropertyChanged("CatSelected");
//!!!Вот здесь, еще оповещаем об изменении в Name
OnPropertyChanged("Name");
}
}
private string _Name;
public string Name
{
get => _Name;
set
{
_Name = value;
OnPropertyChanged();
}
}
//IDEI
public string Error => String.Empty;
public string this[string columnName]
{
get
{
//В случае если выбрано не животное, то проверки нет
if (NotAnimalSelected) return String.Empty;
if (String.IsNullOrEmpty(Name) || Name.Trim().Length <= 3)
{
return "Кличка не может быть короче 4-х символов";
}
return String.Empty;
}
}
//INPC
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Такая разметка вьюшки
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Margin="20">
<RadioButton Content="Не животное"
GroupName="Animal"
IsChecked="{Binding NotAnimalSelected, Mode=TwoWay}"></RadioButton>
<RadioButton Content="Корова"
GroupName="Animal"
IsChecked="{Binding CowSelected, Mode=TwoWay}"></RadioButton>
<RadioButton Content="Собака"
GroupName="Animal"
IsChecked="{Binding DogSelected, Mode=TwoWay}"></RadioButton>
<RadioButton Content="Кошка"
GroupName="Animal"
IsChecked="{Binding CatSelected, Mode=TwoWay}"></RadioButton>
</StackPanel>
<StackPanel Grid.Row="1">
<TextBlock Text="Кличка"
Margin="100,0,0,0" />
<TextBox Width="200"
Height="23"
Text="{Binding Name, ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
Пример работы
P.S. После уточняющего комментария был добавлен в свойство AnimalType вызов OnPropertyChanged("Name"); что заставляет сработать проверку введенных данных в это поле.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей