使用WPF验证规则和禁用“保存”按钮

30

在我的页面上,有几个文本框必须填写才能点击保存按钮。

<TextBox...

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

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

我的规则有效,文本框周围有红色边框,直到我输入值为止。现在我想将此验证规则添加到我的其他文本框中。

如何在页面没有验证错误时禁用保存按钮?我不确定要检查什么。

10个回答

19

2
精彩的解释。谢谢! - Ignacio Soler Garcia
5
很遗憾,所提及的例子并没有详细描述如何启用/禁用按钮。 - trapicki
14
在回答中提供关键部分会很好。如果所提到的网站消失了,这个答案就没有用了。 - trapicki
9
问题是关于如何在使用ValidationRules时禁用按钮。但所引用的示例是关于使用IDataErrorInfo - 另一种验证方式。 - Prince
1
请查看此视频 https://www.youtube.com/watch?v=OOHDie8BdGI,它展示了如何使用IDataErrorInfo来禁用/启用按钮的示例。 - Renato Pereira

18

在视图的代码后台,您可以像这样连接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;
}
这假设你会收到删除通知(如果在无效的情况下删除有问题的元素,则不会发生这种情况)。

如果没有任何视图,只有一个窗口,该如何处理? - NoWar
1
谁还在使用代码后端?如何在视图模型中实现这个功能? - Jordan
2
为了接收错误通知,我必须在需要验证的元素上添加 NotifyOnValidationError="True"<Binding Path ="LastName" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">(默认值为 false)。 - ZwoRmi

7

4
你没有回答“如何禁用保存按钮”的问题。请提供代码。你所提到的示例“绑定到(Validation.Errors)[0]而不会创建调试信息”,没有任何代码来禁用保存按钮。 - NoWar

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

2

这是一个辅助方法,它会跟踪依赖对象(及其所有子对象)上的验证错误,并调用委托来通知更改。它还会跟踪移除带有验证错误的子对象。

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

2

这是关于IT技术的内容,您需要在后台代码中检查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());

2
因为还没有找到,所以在开发者的回答中进行了适应,以防链接消失:
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--;
}

1
我已经尝试了上述的几个解决方案,但是它们都没有对我起作用。

我的简单问题

我有一个简单的输入窗口,要求用户提供一个URI,如果TextBox的值不是有效的Uri,那么Okay按钮应该被禁用。

我的简单解决方案

以下是对我有效的方法:

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

1

只需从System.ComponentModel.IDataErrorInfo继承您的ViewModel以进行验证,并从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;
            }
        }
}

1
请你把代码中所有的闭合括号都加上,可以吗? - A.L

0

该网站拥有您需要的代码: 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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接