When I first display my screen to the user, I'd rather not have all the validation messages show up for required fields and such before the user has had a chance to fill in any of the form fields. I've set the UpdateSourceTrigger
on my bindings to LostFocus
, but the errors are still displayed the first time the control is shown. Is there any way to get around this?
当我第一次向用户显示我的屏幕时,我宁愿在用户有机会填写任何表单字段之前没有显示所有必需字段的验证消息。我在我的绑定LostFocus上设置了UpdateSourceTrigger,但是第一次显示控件时仍会显示错误。有没有办法解决这个问题?
XAML:
XAML:
<TextBox Text="{Binding Path=OpeningOdometer, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" />
ViewModel:
视图模型:
[Required(ErrorMessage = "Please enter the opening odometer.")]
[Range(0, Double.MaxValue, ErrorMessage = "Opening Odometer must be a positive number")]
public string OpeningOdometer
{
get { return _openingOdometer; }
set
{
_openingOdometer = value;
NotifyOfPropertyChange(() => OpeningOdometer);
}
}
// Implementation of IDataErrorInfo
public string this[string columnName]
{
get
{
// This uses the System.ComponentModel.DataAnnotations placed on
// the OpeningOdometer property to produce an error string
// if the value of the property is in violation of any of the
// annotated rules.
return _valHelper.GetErrorsForProperty(columnName, this);
}
}
3 个解决方案
#1
8
I don't put validation logic in the indexer. That turns control over the timing of validation to the view. In your scheme, the view triggers validation whenever it asks for the property's error info. I don't know every circumstance in which that's going to happen and I bet you don't either.
我没有将验证逻辑放在索引器中。这将控制验证的时间到视图。在您的方案中,视图会在询问属性的错误信息时触发验证。我不知道会发生什么情况,我敢打赌你也不会。
Instead, I put a property's validation logic (more accurately, the call to the validation function) in its setter. I store the error message in a dictionary keyed on property name, and have the indexer look up the error message for the property.
相反,我在其setter中放置了属性的验证逻辑(更准确地说,是对验证函数的调用)。我将错误消息存储在键入属性名称的字典中,并让索引器查找该属性的错误消息。
By default, then, the error message is up to date with the property's current value. Whenever the view updates a property and requests its new error info, it'll get the right answer.
默认情况下,错误消息是最新的属性的当前值。每当视图更新属性并请求其新的错误信息时,它都会得到正确的答案。
But you also have pretty fine-grained control over what's actually in that dictionary. If you want a property to show up in the UI as valid, just clear its error message in the dictionary (and raise PropertyChanged
, so the UI will know to get the new error message). Or you can set the properties' backing fields instead of the properties themselves, bypassing validation, when you construct the view model object.
但是你也可以对字典中的实际内容进行相当精细的控制。如果您希望UI中的属性显示为有效,只需在字典中清除其错误消息(并引发PropertyChanged,以便UI知道获取新的错误消息)。或者,您可以在构造视图模型对象时设置属性的支持字段而不是属性本身,从而绕过验证。
#2
2
You can get better answers if you make an effort to post a snippet of your relevant code/XAML. It would make it easier to reproduce and eliminate much of the guesswork.
如果您努力发布相关代码/ XAML的片段,则可以获得更好的答案。这样可以更容易地重现并消除大部分猜测。
Try setting ValidatesOnTargetUpdated="False"
on your validation rules and see if that helps.
尝试在验证规则上设置ValidatesOnTargetUpdated =“False”,看看是否有帮助。
#3
2
Just to indicate how I handled this utilising IDataErrorInfo
...
只是为了说明我是如何利用IDataErrorInfo处理这个...
I put a call to a new method called OnDataUpdated()
in each setter of my view-bound property, such as:
我在我的视图绑定属性的每个setter中调用了一个名为OnDataUpdated()的新方法,例如:
private string username;
public string Username
{
get { return username; }
set
{
username = value;
OnDataUpdated();
}
}
private string password;
public string Password
{
get { return password; }
set
{
password = value;
OnDataUpdated();
}
}
Then inside OnDataUpdated()
mark a private field boolean as true
indicating data has changed for the first time (FormType
was only necessary for my business case):
然后在OnDataUpdated()内部标记一个私有字段boolean为true,表示数据第一次发生了变化(FormType只是我业务案例所必需的):
private void OnDataUpdated()
{
dataChanged = true;
// .. Any other universal RaisePropertyChanged() events you may want to call to get your UI into sync. Eg. RaisePropertyChanged(() => CanConfirm);
}
Then in my IDataErrorInfo
indexer property I do the following (I split it out so 'ValidForm()' can be called manually to perform form validation too.
然后在我的IDataErrorInfo索引器属性中,我执行以下操作(我将其拆分,因此可以手动调用'ValidForm()'以执行表单验证。
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Username")
{
// If other payment amounts have fully paid for balance, and cash amount has been entered, deny
if (!ValidForm(FormType.Step1, columnName))
result = "Please enter the username field.";
}
else if (columnName == "Password")
{
if (!ValidForm(FormType.Step1, columnName))
result = "Please enter the password field.";
}
return result;
}
}
/// <summary>
/// Test if valid form.
/// </summary>
/// <param name="formType">Specify which form we should validate.</param>
/// <param name="columnName">If ommitted, entire form will be validated.</param>
/// <returns></returns>
private bool ValidForm(FormType formType, string columnName = null)
{
// This field is used to denote when data has changed on the form.
// If data has changed, we know we can activate any form validation.
// We do not activate the form validation until after a user has typed
// something in at least.
if (!dataChanged) return true;
var errors = false;
if (formType == FormType.Step1 && ((string.IsNullOrEmpty(columnName) || columnName == "Username") && string.IsNullOrEmpty(Username)))
errors = true;
if (formType == FormType.Step1 && ((string.IsNullOrEmpty(columnName) || columnName == "Password") && string.IsNullOrEmpty(Password)))
errors = true;
return !errors;
}
Works beautifully. Now I only have validation styles appearing after a user edits the form.
工作得很漂亮。现在,我只在用户编辑表单后出现验证样式。
If you want some extra icing on the cake, you can comment in my RaisePropertyChanged(() => CanConfirm);
in the OnDataUpdated()
method and bind that to your Confirm Button IsEnabled={Binding CanConfirm}
with the associated property:
如果你想要一些额外的锦上添花,你可以在我的RaisePropertyChanged(()=> CanConfirm)中评论;在OnDataUpdated()方法中,并使用相关属性将其绑定到您的确认按钮IsEnabled = {Binding CanConfirm}:
/// <summary>
/// Can the user confirm step 1?
/// </summary>
public bool CanConfirm
{
get { return ValidForm(FormType.Step1); }
}
and your button will only be enabled when your form is valid too. :)
并且只有在表单有效时才会启用您的按钮。 :)
Enjoy! and best of luck with the behemoth that is WPF.
请享用!祝WPF的庞然大物好运。
#1
8
I don't put validation logic in the indexer. That turns control over the timing of validation to the view. In your scheme, the view triggers validation whenever it asks for the property's error info. I don't know every circumstance in which that's going to happen and I bet you don't either.
我没有将验证逻辑放在索引器中。这将控制验证的时间到视图。在您的方案中,视图会在询问属性的错误信息时触发验证。我不知道会发生什么情况,我敢打赌你也不会。
Instead, I put a property's validation logic (more accurately, the call to the validation function) in its setter. I store the error message in a dictionary keyed on property name, and have the indexer look up the error message for the property.
相反,我在其setter中放置了属性的验证逻辑(更准确地说,是对验证函数的调用)。我将错误消息存储在键入属性名称的字典中,并让索引器查找该属性的错误消息。
By default, then, the error message is up to date with the property's current value. Whenever the view updates a property and requests its new error info, it'll get the right answer.
默认情况下,错误消息是最新的属性的当前值。每当视图更新属性并请求其新的错误信息时,它都会得到正确的答案。
But you also have pretty fine-grained control over what's actually in that dictionary. If you want a property to show up in the UI as valid, just clear its error message in the dictionary (and raise PropertyChanged
, so the UI will know to get the new error message). Or you can set the properties' backing fields instead of the properties themselves, bypassing validation, when you construct the view model object.
但是你也可以对字典中的实际内容进行相当精细的控制。如果您希望UI中的属性显示为有效,只需在字典中清除其错误消息(并引发PropertyChanged,以便UI知道获取新的错误消息)。或者,您可以在构造视图模型对象时设置属性的支持字段而不是属性本身,从而绕过验证。
#2
2
You can get better answers if you make an effort to post a snippet of your relevant code/XAML. It would make it easier to reproduce and eliminate much of the guesswork.
如果您努力发布相关代码/ XAML的片段,则可以获得更好的答案。这样可以更容易地重现并消除大部分猜测。
Try setting ValidatesOnTargetUpdated="False"
on your validation rules and see if that helps.
尝试在验证规则上设置ValidatesOnTargetUpdated =“False”,看看是否有帮助。
#3
2
Just to indicate how I handled this utilising IDataErrorInfo
...
只是为了说明我是如何利用IDataErrorInfo处理这个...
I put a call to a new method called OnDataUpdated()
in each setter of my view-bound property, such as:
我在我的视图绑定属性的每个setter中调用了一个名为OnDataUpdated()的新方法,例如:
private string username;
public string Username
{
get { return username; }
set
{
username = value;
OnDataUpdated();
}
}
private string password;
public string Password
{
get { return password; }
set
{
password = value;
OnDataUpdated();
}
}
Then inside OnDataUpdated()
mark a private field boolean as true
indicating data has changed for the first time (FormType
was only necessary for my business case):
然后在OnDataUpdated()内部标记一个私有字段boolean为true,表示数据第一次发生了变化(FormType只是我业务案例所必需的):
private void OnDataUpdated()
{
dataChanged = true;
// .. Any other universal RaisePropertyChanged() events you may want to call to get your UI into sync. Eg. RaisePropertyChanged(() => CanConfirm);
}
Then in my IDataErrorInfo
indexer property I do the following (I split it out so 'ValidForm()' can be called manually to perform form validation too.
然后在我的IDataErrorInfo索引器属性中,我执行以下操作(我将其拆分,因此可以手动调用'ValidForm()'以执行表单验证。
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Username")
{
// If other payment amounts have fully paid for balance, and cash amount has been entered, deny
if (!ValidForm(FormType.Step1, columnName))
result = "Please enter the username field.";
}
else if (columnName == "Password")
{
if (!ValidForm(FormType.Step1, columnName))
result = "Please enter the password field.";
}
return result;
}
}
/// <summary>
/// Test if valid form.
/// </summary>
/// <param name="formType">Specify which form we should validate.</param>
/// <param name="columnName">If ommitted, entire form will be validated.</param>
/// <returns></returns>
private bool ValidForm(FormType formType, string columnName = null)
{
// This field is used to denote when data has changed on the form.
// If data has changed, we know we can activate any form validation.
// We do not activate the form validation until after a user has typed
// something in at least.
if (!dataChanged) return true;
var errors = false;
if (formType == FormType.Step1 && ((string.IsNullOrEmpty(columnName) || columnName == "Username") && string.IsNullOrEmpty(Username)))
errors = true;
if (formType == FormType.Step1 && ((string.IsNullOrEmpty(columnName) || columnName == "Password") && string.IsNullOrEmpty(Password)))
errors = true;
return !errors;
}
Works beautifully. Now I only have validation styles appearing after a user edits the form.
工作得很漂亮。现在,我只在用户编辑表单后出现验证样式。
If you want some extra icing on the cake, you can comment in my RaisePropertyChanged(() => CanConfirm);
in the OnDataUpdated()
method and bind that to your Confirm Button IsEnabled={Binding CanConfirm}
with the associated property:
如果你想要一些额外的锦上添花,你可以在我的RaisePropertyChanged(()=> CanConfirm)中评论;在OnDataUpdated()方法中,并使用相关属性将其绑定到您的确认按钮IsEnabled = {Binding CanConfirm}:
/// <summary>
/// Can the user confirm step 1?
/// </summary>
public bool CanConfirm
{
get { return ValidForm(FormType.Step1); }
}
and your button will only be enabled when your form is valid too. :)
并且只有在表单有效时才会启用您的按钮。 :)
Enjoy! and best of luck with the behemoth that is WPF.
请享用!祝WPF的庞然大物好运。