我已将验证连接到绑定到TextBox
容器的模型。首次打开窗口时,模型为空时会出现验证错误,我不希望在提交窗口或TextBox
中的文本发生更改或焦点丢失之前看到验证错误。
以下是TextBox
:
<TextBox Text="{Binding
Path=Firstname,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"
Width="124"
Height="24"/>
如何实现这一目标?
答案 0 :(得分:22)
这实际上取决于您对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
}
然后,您还可以添加一个验证所有属性的方法(例如在保存期间):
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;
}
更新:
我建议将上面的代码放入基础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;
}
}
在这种情况下,我使用DelegateCommand来触发保存操作,但它可以是任何使方法调用来执行保存的操作。此设置允许初始空状态在UI中显示为有效,但更改或调用Save会更新验证状态。你也可以在实际进行验证的过程中获得更多通用和更复杂的功能,因此它不会最终出现在一个方法中(这里有一些关于类型的假设),但是这样做简化了以便更容易入手
答案 1 :(得分:7)
如果您正在实现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
}
此外,如果要在TextBox.LostFocus上触发验证,请将绑定更改为LostFocus,如下所示:
<TextBox Text="{Binding
Path=Firstname,
UpdateSourceTrigger=LostFocus,
ValidatesOnDataErrors=True}"
Width="124"
Height="24"/>
答案 2 :(得分:4)
在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>
答案 3 :(得分:-1)
我做了什么,我不知道如果这是正确的方法(我很乐意学习,现在是一个机会),但在实体或模型的初始化器中我运行所有验证器。