Использование правил проверки WPF и отключение кнопки "Сохранить"

У меня есть страница, где несколько текстовых полей не могут быть пустыми, прежде чем нажать кнопку Сохранить.

<TextBox...

                <TextBox.Text>
                    <Binding Path ="LastName" UpdateSourceTrigger="PropertyChanged">

                        <Binding.ValidationRules>
                            <local:StringRequiredValidationRule />
                        </Binding.ValidationRules>                              
                    </Binding>
                </TextBox.Text>

Мое правило работает. У меня есть красная рамка вокруг моего текстового поля, пока я не введу значение. Итак, теперь я хочу добавить это правило проверки к другим моим текстовым полям.

Теперь, как отключить кнопку Сохранить, пока на странице нет ошибок? Я не знаю, что проверить, чтобы увидеть, есть ли какие-либо ошибки проверки.

8 ответов

Решение

В коде для представления вы можете подключить Validation.ErrorEvent следующим образом;

this.AddHandler(Validation.ErrorEvent,new RoutedEventHandler(OnErrorEvent)); 

А потом

private int errorCount;
private void OnErrorEvent(object sender, RoutedEventArgs e)
{
    var validationEventArgs = e as ValidationErrorEventArgs;
    if (validationEventArgs  == null)
        throw new Exception("Unexpected event args");
    switch(validationEventArgs.Action)
    {
        case ValidationErrorEventAction.Added:
            {
                errorCount++; break;
            }
        case ValidationErrorEventAction.Removed:
            {
                errorCount--; break;
            }
        default:
            {
                throw new Exception("Unknown action");
            }
    }
    Save.IsEnabled = errorCount == 0;
}

Это предполагает, что вы получите уведомление об удалении (что не произойдет, если вы удалите нарушающий элемент, пока он недействителен).

Вы хотите использовать присоединенное свойство Validation.HasError.

В том же духе у Джоша Смита есть интересное прочтение " Привязка к (Validation.Errors)[0] без создания отладочного выброса".

int count = 0;

private void LayoutRoot_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
    {
        button1.IsEnabled = false;
        count++;
    }
    if (e.Action == ValidationErrorEventAction.Removed)
    {                
        count--;
        if (count == 0) button1.IsEnabled = true;
    }
}

Поскольку он все еще отсутствует, вот адаптация ответа разработчика на случай, если ссылка когда-нибудь исчезнет:

XAML:

<TextBox.Text Validation.Error="handleValidationError">
    <Binding Path ="LastName" 
             UpdateSourceTrigger="PropertyChanged"
             NotifyOnValidationError="True">
        <Binding.ValidationRules>
            <local:StringRequiredValidationRule />
        </Binding.ValidationRules>                              
    </Binding>
</TextBox.Text>
<Button IsEnabled="{Binding HasNoValidationErrors}"/>

CodeBehind / C#:

private int _numberOfValidationErrors;
public bool HasNoValidationErrors => _numberOfValidationErrors = 0;

private void handleValidationError(object sender, ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
        _numberOfValidationErrors++;
    else
        _numberOfValidationErrors--;
}

Вот вспомогательный метод, который отслеживает ошибки проверки объектов зависимостей (и всех его дочерних элементов) и вызывает делегат для уведомления об изменении. Он также отслеживает удаление детей с ошибками проверки.

 public static void AddErrorHandler(DependencyObject element, Action<bool> setHasValidationErrors)
        {
            var errors = new List<Tuple<object, ValidationError>>();

            RoutedEventHandler sourceUnloaded = null;

            sourceUnloaded = (sender, args) =>
                {
                    if (sender is FrameworkElement)
                        ((FrameworkElement) sender).Unloaded -= sourceUnloaded;
                    else
                        ((FrameworkContentElement) sender).Unloaded -= sourceUnloaded;

                    foreach (var error in errors.Where(err => err.Item1 == sender).ToArray())
                        errors.Remove(error);

                    setHasValidationErrors(errors.Any());
                };

            EventHandler<ValidationErrorEventArgs> errorHandler = (_, args) =>
                {
                    if (args.Action == ValidationErrorEventAction.Added)
                    {
                        errors.Add(new Tuple<object, ValidationError>(args.OriginalSource, args.Error));

                        if (args.OriginalSource is FrameworkElement)
                            ((FrameworkElement)args.OriginalSource).Unloaded += sourceUnloaded;
                        else if (args.OriginalSource is FrameworkContentElement)
                            ((FrameworkContentElement)args.OriginalSource).Unloaded += sourceUnloaded;
                    }
                    else
                    {
                        var error = errors
                            .FirstOrDefault(err => err.Item1 == args.OriginalSource && err.Item2 == args.Error);

                        if (error != null) 
                            errors.Remove(error);
                    }

                    setHasValidationErrors(errors.Any());
                };


            System.Windows.Controls.Validation.AddErrorHandler(element, errorHandler);
        } 

Это вам нужно проверить свойство элемента управления HasError из кода поведения

и сделать этот код в нажатии кнопки Сохранить

 BindingExpression bexp = this.TextBox1.GetBindingExpression(TextBox.TextProperty);
bexp.UpdateSource(); // this to refresh the binding and see if any error exist 
bool hasError = bexp.HasError;  // this is boolean property indique if there is error 

MessageBox.Show(hasError.ToString());

Просто наследуйте вашу ViewModel от System.ComponentModel.IDataErrorInfo для проверки и от INotifyPropertyChanged для уведомления кнопки

сделать собственность:

    public bool IsValid
    {
        get
        {
            if (this.FloorPlanName.IsEmpty())
                return false;
            return true;
        }
    }

в xaml подключи его к кнопке

<Button Margin="4,0,0,0" Style="{StaticResource McVMStdButton_Ok}" Click="btnDialogOk_Click" IsEnabled="{Binding IsValid}"/>

в переопределениях IDataErrorInfo уведомить кнопку

public string this[string columnName]{
        get
        {
            switch (columnName)
            {
                case "FloorPlanName":
                    if (this.FloorPlanName.IsEmpty())
                    {
                        OnPropertyChanged("IsValid");
                        return "Floor plan name cant be empty";
                    }
                    break;
            }
        }
}

Я пробовал несколько решений, указанных выше; однако, ни один из них не работал для меня.

Моя простая проблема

У меня есть простое окно ввода, которое запрашивает URI от пользователя, если TextBox значение не является действительным Uri тогда Okay Кнопка должна быть отключена.

Мое простое решение

Вот что сработало для меня:

CommandBindings.Add(new CommandBinding(AppCommands.Okay,
            (sender, args) => DialogResult = true,
            (sender, args) => args.CanExecute = !(bool) _uriTextBoxControl.GetValue(Validation.HasErrorProperty)));

На этом веб-сайте есть код, который вы ищете:https://www.wpfsharp.com/2012/02/03/how-to-disable-a-button-on-textbox-validationerrors-in-wpf/

Для потомков код кнопки должен выглядеть так, если вы используете переопределение ValidationRule в полях ввода:

<Button Content="<NameThisButton>" Click="<MethodToCallOnClick>" >
                <Button.Style>
                    <Style TargetType="{x:Type Button}">
                        <Setter Property="IsEnabled" Value="false" />
                        <Style.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>                                    
                                    <Condition Binding="{Binding ElementName=<TextBoxName>, Path=(Validation.HasError)}" Value="false" />
                                    <Condition Binding="{Binding ElementName=<TextBoxName>, Path=(Validation.HasError)}" Value="false" />
                                </MultiDataTrigger.Conditions>
                                <Setter Property="IsEnabled" Value="true" />
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
Другие вопросы по тегам