在 WPF 中同时验证多个控件

Tom*_*Tom 5 c# passwords validation wpf mvvm

我有一个包含两个密码字段的表单 - 一个是用户输入密码的地方,另一个是用户必须重新输入该密码才能确认的地方。验证用于确认两个密码匹配 - 如果匹配,则启用一个按钮以允许用户继续:

<PasswordBox Name="P1Box" src:PasswordBoxAssistant.BindPassword="True">
    <src:PasswordBoxAssistant.BoundPassword>
        <Binding Source="{StaticResource mybinding}" Path="Password.P1" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
            <Binding.ValidationRules>
                <DataErrorValidationRule ValidatesOnTargetUpdated="True"/>
            </Binding.ValidationRules>
        </Binding>
    </src:PasswordBoxAssistant.BoundPassword>
</PasswordBox>
Run Code Online (Sandbox Code Playgroud)

按钮样式:

<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
    <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding ElementName=P1Box, Path=(Validation.HasError)}" Value="false"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="IsEnabled" Value="true"/>
    </MultiDataTrigger>
</Style.Triggers>
Run Code Online (Sandbox Code Playgroud)

我的问题是用户可以更改第一个框中的密码,并且不会强制第二个密码框重新验证。例如,如果第一个密码输入为“password”,而用户在第二个框中输入“password”,则验证通过并启用按钮。如果用户随后将原始密码框更改为“PASSWORD”,则两个框都将保持验证状态 - 原始框是因为对非空密码没有限制,第二个是因为没有任何内容强制更新验证。

我的密码框使用此处概述的附加属性来允许绑定到密码。因此,我找不到以本解决方案中表达的方式在代码隐藏中访问它的方法(因为 PasswordBox.Password 本身不是依赖属性)或者,也许它只是不适用于附加属性 - 下面的代码没有做任何事情:

P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource();
Run Code Online (Sandbox Code Playgroud)

我有一个自定义类,它继承 IDataErrorInfo 以允许在两个控件之间进行验证 - 绑定是一个 PasswordData 对象,并且密码框设置为 PasswordData.P1 和 PasswordData.P2:

public class PasswordData : IDataErrorInfo
{
    public string P1 { get; set; }
    public string P2 { get; set; }
    public string Error { get { return string.Empty; } }
    public string this[string propertyName]
    {
        get
        {
            string ret;
            if (propertyName == "P1")
            {
            if (P1 == null || P1 == "")
                ret = "Password cannot be null or empty.";
            else
                ret = "";
            }
            else if (propertyName == "P2")
            {
            if (P2 == null || P2 == "")
                ret = "Password cannot be null or empty.";
            else if (P2 != P1)
                ret = "The passwords do not match.";
            else
                ret = "";
            }
            return ret;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我曾尝试在 PasswordChanged 事件期间跳入,创建新的 PasswordData 并重新分配绑定。这解决了验证问题,但密码框中的插入符号总是在最开始,破坏了输入的任何数据。

我想要一个仅 xaml 的解决方案,但背后的代码是完全可以接受的。如果这很重要,我正在使用 .Net 4.0。

编辑:

好的,事实证明我在 xaml 中错误输入了事件处理程序,并且该解决方案确实有效:

private void PasswordChanged(object sender, RoutedEventArgs e)
{
    binding.Pass.P1 = ((PasswordBox)sender).Password;
    P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource();
}
Run Code Online (Sandbox Code Playgroud)

我必须手动更新绑定,因为在更新绑定之前会触发事件。

我很好奇是否有正确的 XAML 唯一方法使用验证规则、IDataErrorInfo 或其他一些方法来在控件之间进行绑定,而不必挂钩事件并手动更新。

JDB*_*JDB 3

对于更复杂的验证,通常需要将验证推入 ViewModel,甚至在许多情况下推入模型。IDataErrorInfo 是一个好的开始。

以下是有关该主题的优秀文章的链接:
http://msdn.microsoft.com/en-us/magazine/ff714593.aspx

  • 这并不是 WPF 的真正限制 - 涉及的问题是:您想要验证什么?当您验证字符串是否代表数值时,验证的重点是狭隘的。但是,如果您要验证表单上的一个值是否等于表单上的另一个值,那么您并不是在验证各个值,而是在验证整个表单。表单有效或无效取决于这些文本框中的值。当然,在 WPF 中,“表单”通常是视图模型。验证视图模型时,IDataErrorInfo 是一个不错的选择。 (2认同)