Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在WPF中同时验证多个控件_C#_Wpf_Validation_Mvvm_Passwords - Fatal编程技术网

C# 在WPF中同时验证多个控件

C# 在WPF中同时验证多个控件,c#,wpf,validation,mvvm,passwords,C#,Wpf,Validation,Mvvm,Passwords,我有一个带有两个密码字段的表单——一个是用户输入密码的字段,另一个是用户必须重新输入密码才能确认的字段。验证用于确认两个密码是否匹配-如果匹配,则会启用一个按钮以允许用户继续: <PasswordBox Name="P1Box" src:PasswordBoxAssistant.BindPassword="True"> <src:PasswordBoxAssistant.BoundPassword> <Binding Source="{Sta

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

<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>
我有一个自定义类,它继承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;
        }
    }
}
我已尝试在PasswordChanged事件期间跳入,创建新的PasswordData并重新分配绑定。这解决了验证问题,但是密码框中的插入符号总是在最开始的时候,会破坏输入的任何数据

我想要一个只有xaml的解决方案,但是代码隐藏是完全可以接受的。如果有必要的话,我正在使用.NET4.0

编辑:

 valid = this.window.Revalidate<TextBox>(); // where this.window is a reference to our window.
好的,结果是我在xaml中输入了错误的事件处理程序,解决方案实际上是有效的:

private void PasswordChanged(object sender, RoutedEventArgs e)
{
    binding.Pass.P1 = ((PasswordBox)sender).Password;
    P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource();
}
我必须手动更新绑定,因为在更新绑定之前会触发事件


我很好奇,是否有一种合适的、仅使用XAML的方法,使用验证规则、IDataErrorInfo或其他方法在控件之间进行绑定,而不必挂接到事件中并手动更新。

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

这里有一个关于这个主题的优秀文章的链接:

这可能会有所帮助。我看到很多文章在模型类中使用了数据注释。当您将模型绑定到WPF中的上下文时,您可能会注意到在未输入任何数据之前,验证会触发

这可能是有问题的,迭代绑定表达式可能是一件痛苦的事情。所以我连接了几个扩展来做这个

这将得到一个可枚举的文本框。DependencyObject可能是您的窗口或网格。要引用您的窗口,请使用var window=window.GetWindow(this)。如果愿意,还可以按名称使用控件,如网格。

  public static IEnumerable<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject
    {
        if (obj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }   
        public static Boolean Revalidate<T>(this Window depObj) where T : DependencyObject
    {
        bool isValid = true;
        foreach (T obj in FindVisualChildren<T>(depObj))
        {
            var name = typeof(T).Name.ToLower();
            BindingExpression exp = null;

            switch (name)
            {
                case "textbox":
                    var tb = obj as TextBox;
                    exp = tb.GetBindingExpression(TextBox.TextProperty);
                    exp.UpdateSource();
                    if (Validation.GetHasError(tb))
                        isValid = false;  
                    break;           
            }                             
        }

        return isValid;
    }
if(!valid){
   return; // you could update a message or something as well obviously.
}

+1个写得很好的问题。我将继续并接受这个作为答案-它似乎只是WPF的一个限制,验证在控件之间不起作用。谢谢你!这并不是WPF的真正局限性——所涉及的问题是:您想验证什么?当您验证字符串是否表示数值时,验证的重点是狭义的。但是,如果要验证窗体上的一个值是否等于窗体上的另一个值,则不是要验证单个值,而是要验证整个窗体。根据这些文本框中的值,表单有效或无效。当然,在WPF中,“表单”通常是视图模型。在验证视图模型时,IDataErrorInfo是一个很好的选择。这个答案对我很有帮助。我有一个组合框,需要在选择更改时验证文本框,这是我的工作代码:
private void Selector_OnSelectionChanged(对象发送方,SelectionChangedEventArgs e){BindingExpression exp=IdTextBox.GetBindingExpression(textbox.TextProperty);exp.UpdateSource();}
 valid = this.window.Revalidate<TextBox>(); // where this.window is a reference to our window.
if(!valid){
   return; // you could update a message or something as well obviously.
}