WPF 文本框验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2079552/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF TextBox Validation
提问by Burt
I have validation hooked up to a model that is bound to the TextBox
container. When the window is first opened validation errors appear as the model is empty, I do not want to see validation errors until submit of the window or the text in the TextBox
has changed or on lost focus.
我已将验证连接到绑定到TextBox
容器的模型。当窗口第一次打开时,由于模型为空而出现验证错误,我不想看到验证错误,直到提交窗口或文本TextBox
更改或失去焦点。
Here is the TextBox
:
这是TextBox
:
<TextBox Text="{Binding
Path=Firstname,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"
Width="124"
Height="24"/>
How can this be achieved?
如何做到这一点?
回答by John Bowen
This really depends on your implementation of IDataErrorInfo. If you base it around a Dictionary of error messages you can control when validation runs that adds to that list. You would normally want to do that from your property setters (like whenever you call PropertyChange), here calling CheckValidationState:
这实际上取决于您对 IDataErrorInfo 的实现。如果您基于错误消息字典,则可以控制添加到该列表的验证运行的时间。您通常希望从您的属性设置器中执行此操作(就像您每次调用 PropertyChange 时一样),这里调用 CheckValidationState:
public string this[string columnName]
{
get
{
return ValidateProperty(columnName);
}
}
public Dictionary<string, string> Errors { get; private set; }
protected void SetError(string propertyName, string errorMessage)
{
Debug.Assert(!String.IsNullOrEmpty(propertyName), "propertyName is null or empty.");
if (String.IsNullOrEmpty(propertyName))
return;
if (!String.IsNullOrEmpty(errorMessage))
{
if (Errors.ContainsKey(propertyName))
Errors[propertyName] = errorMessage;
else
Errors.Add(propertyName, errorMessage);
}
else if (Errors.ContainsKey(propertyName))
Errors.Remove(propertyName);
NotifyPropertyChanged("Errors");
NotifyPropertyChanged("Error");
NotifyPropertyChanged("Item[]");
}
protected virtual string ValidateProperty(string propertyName)
{
return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null;
}
protected virtual bool CheckValidationState<T>(string propertyName, T proposedValue)
{
// your validation logic here
}
You can then also include a method that validates all of your properties (like during a save):
然后,您还可以包含一个验证所有属性的方法(例如在保存期间):
protected bool Validate()
{
if (Errors.Count > 0)
return false;
bool result = true;
foreach (PropertyInfo propertyInfo in GetType().GetProperties())
{
if (!CheckValidationState(propertyInfo.Name, propertyInfo.GetValue(this, null)))
result = false;
NotifyPropertyChanged(propertyInfo.Name);
}
return result;
}
UPDATE:
更新:
I would recommend putting the above code into a base ViewModel class so you can reuse it. You could then create a derived class like this:
我建议将上述代码放入基础 ViewModel 类中,以便您可以重用它。然后你可以像这样创建一个派生类:
public class SampleViewModel : ViewModelBase
{
private string _firstName;
public SampleViewModel()
{
Save = new DelegateCommand<object>(SaveExecuted);
}
public DelegateCommand<object> Save { get; private set; }
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
return;
CheckValidationState("FirstName", value);
_firstName = value;
NotifyPropertyChanged("FirstName");
}
}
public void SaveExecuted(object obj)
{
bool isValid = Validate();
MessageBox.Show(isValid ? "Saved" : "Validation Error. Save canceled"); // TODO: do something appropriate to your app here
}
protected override bool CheckValidationState<T>(string propertyName, T proposedValue)
{
// your validation logic here
if (propertyName == "FirstName")
{
if (String.IsNullOrEmpty(proposedValue as String))
{
SetError(propertyName, "First Name is required.");
return false;
}
else if (proposedValue.Equals("John"))
{
SetError(propertyName, "\"John\" is not an allowed name.");
return false;
}
else
{
SetError(propertyName, String.Empty); // clear the error
return true;
}
}
return true;
}
}
In this case I'm using a DelegateCommand to trigger the save operation but it could be anything that makes a method call to do the saving. This setup allows for the initial empty state to show up as valid in the UI but either a change or a call to Save updates the validation state. You can also get a lot more general and more complicated in the way you actually do the validation so it doesn't all end up in one method (here with some assumptions about the type) but this is simplified to make it easier to start with.
在这种情况下,我使用 DelegateCommand 来触发保存操作,但它可以是任何调用方法来执行保存的操作。此设置允许初始空状态在 UI 中显示为有效,但更改或调用 Save 会更新验证状态。您还可以在实际进行验证的方式中获得更通用和更复杂的方法,因此它不会全部以一种方法结束(这里有一些关于类型的假设)但这是简化的,以便更容易开始.
回答by Brent
If you are implementing IDataErrorInfo, I've achieved this by checking for null values in the implementation of the validation logic. When creating a new window, checking for null will prevent your validation logic from firing. For example:
如果您正在实现 IDataErrorInfo,我已经通过在验证逻辑的实现中检查空值来实现这一点。创建新窗口时,检查 null 将阻止您的验证逻辑触发。例如:
public partial class Product : IDataErrorInfo
{
#region IDataErrorInfo Members
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
if (columnName == "ProductName")
{
// Only apply validation if there is actually a value
if (this.ProductName != null)
{
if (this.ProductName.Length <= 0 || this.ProductName.Length > 25)
return "Product Name must be between 1 and 25 characters";
}
}
return null;
}
}
#endregion
}
Also, if you want to fire the validation on TextBox.LostFocus, change your binding to LostFocus, like follows:
此外,如果您想在 TextBox.LostFocus 上触发验证,请将您的绑定更改为 LostFocus,如下所示:
<TextBox Text="{Binding
Path=Firstname,
UpdateSourceTrigger=LostFocus,
ValidatesOnDataErrors=True}"
Width="124"
Height="24"/>
回答by Brent
in your app.xaml file, you need to use custom textbox style for textbox validation without any third party component.
在您的 app.xaml 文件中,您需要使用自定义文本框样式进行文本框验证,而无需任何第三方组件。
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid Name="test">
<Border Background="{StaticResource TextBackColor}"
BorderBrush="#FF888888"
x:Name="Bd"
CornerRadius="1"
BorderThickness="1">
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<Image Name="ErrorImage"
Width="15"
Height="15"
Margin="0,0,4,0"
Source="Images/validate.png"
HorizontalAlignment="Right">
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
回答by Shimmy Weitzhandler
What I do, I donno if this is the correct way (I would be glad to learn, now is a chance), but in the initializer of the entity or the model I run all the validators.
我做什么,我不知道这是否是正确的方法(我很高兴学习,现在是一个机会),但是在实体或模型的初始化程序中,我运行了所有验证器。