wpf - 如何禁用 WPF 对话框确定按钮以响应自定义 ValidationRule 类确定的无效输入?

标签 wpf mvvm error-handling binding

我有一个简单的 WPF 对话框,允许用户输入名称。我们正在使用没有任何代码隐藏文件的 Mvvm 方法。我需要验证输入,并且只有在输入有效时才启用 OK 按钮。我目前正在使用 View 中的自定义错误模板和 ValidationRule 类的自定义实现进行验证。

对话框中的文本框定义为:

    <TextBox Width="250" Height="25" Margin="5"
             Validation.ErrorTemplate="{StaticResource customErrorTemplate}">
        <TextBox.Text>
            <Binding Path="WitnessName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <ValidationRules:NameRule />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

NameRule 定义为:
public class NameRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        var isValid = (value as string == null) || Regex.IsMatch(value.ToString(), @"^[\p{L} \.'\-]+$");

        return new ValidationResult(isValid, "Name can contain only letters, apostrophes and hyphens.");
    }
}

OK 按钮 IsEnabled 属性绑定(bind)到 View 模型上的 IsOkEnabled 属性,仅当输入有效时才为 true。
<Button Name="OnOkClick" Margin="5" IsEnabled="{Binding IsOkEnabled}">OK</Button>

通过监视 WitnessName PropertyChanged 事件来更新 IsOkEnabled 属性以响应 WitnessName 文本中的更改。
public string WitnessName
{
    get
    {
        return this.witnessName;
    }

    set
    {
        this.witnessName = value;
        this.NotifyOfPropertyChange(() => this.IsOkEnabled);
    }
}

问题在于,在 View 中进行验证时,当输入无效输入时,不会在 vew 模型上触​​发 PropertyChanged 事件,因此不会更新 IsOkEnabled 属性并且“确定”按钮保持启用状态。

有没有办法强制更新 IsOkEnabled 属性以响应我当前实现中甚至无效的输入?

我都看过Karl Shifflet'sJosh Smith's建议,但都没有使用(据我所知) View 错误模板,我想用一个来提供视觉反馈。

更新:按照 Danny 的建议使用绑定(bind)的 ICommand 尝试此操作

我已经通过创建一个特定于 OK 按钮的 VM 来尝试这个,它实现了 ICommand。 OkButtonViewModel 具有保存对话 VM 的属性(由 IoC 和 unity 设置),并且 CanExecute 和 Execute 实现引用此 VM 上的相关属性/方法。

在 OkButtonViewModel 中:
public bool CanExecute(object parameter)
{
    return this.witnessDialogue.IsValid;
}

public void Execute(object parameter)
{
    this.witnessDialogue.OnOkClick();
}

如何将我的按钮绑定(bind)到这个虚拟机而不是对话虚拟机?如果 OkButtonViewModel 是对话 VM 上的属性,我可以做到这一点,但当依赖关系相反时,我可以做到这一点,它需要按钮才能使用对话 VM 上的实现。

最佳答案

摆脱 OnClick 事件处理程序,而是使用对命令的绑定(bind)。
使用返回 Model.IsValid 的 CanExecute 方法在 VM 中注册命令。

如果验证失败是在 View 与 View 模型的绑定(bind)中,
那么 View 模型仍然有效 - 因为它没有存储更新(由于类型不匹配、范围检查等)。

在这种情况下,请考虑在抛出异常之前将错误状态存储在 VM 中,然后在成功设置相同属性时清除错误状态。

修改后的代码:

XAML:
(注意 ValidationRules:NameRule 实例的作用域是在 TextBox 中,其他用法会收到另一个不同字段值的实例)

<TextBox Width="250" Height="25" Margin="5"
         Validation.ErrorTemplate="{StaticResource customErrorTemplate}">
    <TextBox.Text>
        <Binding Path="WitnessName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ValidationRules:NameRule x:Name="nameValidator" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

验证规则:
public class NameRule : ValidationRule, INotifyPropertyChanged
{
   public bool HasFailed // set default of field behind to false
   {
      get; // change to support INotifyPropertyChanged
      set; // change to support INotifyPropertyChanged
   }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        var isValid = (value as string == null) || Regex.IsMatch(value.ToString(), @"^[\p{L} \.'\-]+$");

        HasFailed = !isValid;

        return new ValidationResult(isValid, "Name can contain only letters, apostrophes and hyphens.");
    }
}

命令:
public bool CanExecute(object parameter)
{
    return this.witnessDialogue.IsValid && !this.witnessDialogue.nameValidator.HasFailed;
}

public void Execute(object parameter)
{
    this.witnessDialogue.OnOkClick();
}

关于wpf - 如何禁用 WPF 对话框确定按钮以响应自定义 ValidationRule 类确定的无效输入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4783082/

相关文章:

extjs - Ext JS 5 数据绑定(bind)组合框

c# - 完整的业务线应用程序 - PRISM MEF Entity Framework - 示例?

error-handling - 如何在BizTalk管理控制台中显示来自自定义仿函数类错误的异常消息?

c# - 选中时更改切换按钮的背景颜色

.net - 关于在 WPF 中调试焦点问题的任何提示?

wpf - MVVM:ViewModel 的松散耦合

c# - 在属性更改启动长期运行任务中填充另一个属性

cordova - 来自 try/catch 的 Phonegap 错误对象缺少信息

c++ - 为什么 Windows 不能读取超出 0x1A (EOF) 字符但 Unix 可以?

c# - 观察集合中项目的 PropertyChanged