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