Валидация RadioButton'ов в WPF на основе IDataErrorInfo

247
31 декабря 2018, 14:40

Задача: создать аккуратную систему валидации контролов в 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="не род-&#10;ственник" Style="{StaticResource CheckerText}"/>
    </RadioButton.Content>
</RadioButton>

Проблема: если RadioButton уже стоит в позиции, кроме "не родственник" - все валидируется. Если уже стоит в позиции "не родственник" - не валидируется, как и нужно. Однако, если написать текст, и сменить галочку - то событие валидации не срабатывает. Смена галочки вообще не вызывает это событие, отладчик даже не попадает в индексатор public string this[string columnName].

Подскажите, пожалуйста, как мне заставить VM запускать валидацию текстового поля, если меняется связанные с ним логически RadioButton'ы?

Answer 1

Например, можно сделать 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"); что заставляет сработать проверку введенных данных в это поле.

READ ALSO
Невозможно вывести число

Невозможно вывести число

В данном ниже коде в строке ConsoleWriteLine("{0}", U); U (по словам компилятора) - локальная переменная, которой не присвоено значение, хотя она была инициализирована...

214
Как закрыть главную форму при открытии дочерней?

Как закрыть главную форму при открытии дочерней?

Обработчик кнопки главной формы которая открывает дочернее окно

325
Чтение xml файла в приложении winform(C#)

Чтение xml файла в приложении winform(C#)

Необходимо считать данные с xml файла

215
Не отправляется файл ajax запросом

Не отправляется файл ajax запросом

Очень странная ситуацияКогда запускаю проект в студии, выбираю файл в форме он отправляется и возвращает нужные мне данные

237