C# – Validate Multiple Controls Simultaneously in WPF

cmvvmpasswordsvalidationwpf

I have a form with two password fields – one where the user enters the password, and another where the user has to re-enter that password to confirm. Validation is used to confirm that both passwords match – if they do, a button is enabled to allow the user to proceed:

<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>

Button Style:

<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>

My problem is that the user can alter the password in the first box, and it won't force the second password box to re-validate. For example, if the first password is entered as "password", and the user enters "password" into the second box, the validation passes and the button is enabled. If the user then changes the original password box to "PASSWORD", both boxes stay validated – the original because there are no constraints on a nonempty password, the second because nothing has forced the validation to update.

My password boxes use the attached property outlined here to allow binding to the password. Because of this, I can't find a way to access it in code-behind (since PasswordBox.Password is not a dependency property by itself) in the manner expressed in this solution. Or, perhaps it just doesn't work for the attached properties – the code below didn't do anything:

P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource();

I have a custom class which inherits IDataErrorInfo to allow for validation between the two controls – the binding is a PasswordData object, and the password boxes are set to PasswordData.P1 and 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;
        }
    }
}

I have tried hopping in during the PasswordChanged event, creating a new PasswordData, and reassigning the binding. This solves the validation problem, but the caret in the passwordbox is always at the very beginning, ruining any data entered.

I would like have a xaml-only solution, but code behind is perfectly acceptable. I'm using .Net 4.0, if that matters.

EDIT:

OK, so it turns out I mistyped the event handler in the xaml, and the solution actually works:

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

I have to manually update the binding, because the event fires before the binding is updated.

I am curious if there is a proper, XAML only way using validation rules, IDataErrorInfo, or some other means to have a binding between the controls without having to hook into the events and manually update.

Best Answer

For more complex validation, it is often necessary to push your validation into the ViewModel, or even the Model in many cases. IDataErrorInfo is a good start.

Here's a link to an excellent article on the subject:
http://msdn.microsoft.com/en-us/magazine/ff714593.aspx

Related Topic