正如你可能可以从标题看,我要问的东西已经被问过很多次。 但尽管如此,阅读所有这些其他问题后,我无法找到一个体面的解决我的问题。
我有一个基本的验证模型类:
partial class Player : IDataErrorInfo
{
public bool CanSave { get; set; }
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Firstname")
{
if (String.IsNullOrWhiteSpace(Firstname))
{
result = "Geef een voornaam in";
}
}
if (columnName == "Lastname")
{
if (String.IsNullOrWhiteSpace(Lastname))
{
result = "Geef een familienaam in";
}
}
if (columnName == "Email")
{
try
{
MailAddress email = new MailAddress(Email);
}
catch (FormatException)
{
result = "Geef een geldig e-mailadres in";
}
}
if (columnName == "Birthdate")
{
if (Birthdate.Value.Date >= DateTime.Now.Date)
{
result = "Geef een geldige geboortedatum in";
}
}
CanSave = true; // this line is wrong
return result;
}
}
public string Error { get { throw new NotImplementedException();} }
}
此验证每次属性更改完成(所以每次用户键入文本框中的字符):
<TextBox Text="{Binding CurrentPlayer.Firstname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="137" IsEnabled="{Binding Editing}" Grid.Row="1"/>
这完美的作品。 验证发生时(所述PropertyChanged
用于结合代码在VM上CurrentPlayer属性,它是播放器的目的完成)。
我想现在要做的是禁用当验证失败的保存按钮。
首先,最简单的解决方案,似乎在这个线程被发现:
启用禁用使用IDataErrorInfo的验证过程中保存按钮
- 如果我想跟进接受的解决方案,我不得不写我的验证代码两次,因为我不能简单地使用索引。 写双码绝对不是我想要的东西,所以这不是我的问题的解决方案。
- 在该线程的第二个答案听起来头很看好,但问题是,我有一个要被验证多个字段。 这样一来,一切都依赖于最后检查属性(因此,如果该字段填写正确,
CanSave
将是真实的,即使有其他领域,其依然无效)。
还有一个解决方案,我发现是使用ErrorCount
财产。 但正如我在每个属性的变化验证(所以在每个输入的字符),这是不可能太-我怎么知道何时增加/减少ErrorCount
?
什么是解决这个问题的最好方法是什么?
谢谢
我实现了在显示地图的方法我上面的评论 ,在C#这就是所谓的字典中,我使用匿名方法做了验证:
partial class Player : IDataErrorInfo
{
private delegate string Validation(string value);
private Dictionary<string, Validation> columnValidations;
public List<string> Errors;
public Player()
{
columnValidations = new Dictionary<string, Validation>();
columnValidations["Firstname"] = delegate (string value) {
return String.IsNullOrWhiteSpace(Firstname) ? "Geef een voornaam in" : null;
}; // Add the others...
errors = new List<string>();
}
public bool CanSave { get { return Errors.Count == 0; } }
public string this[string columnName]
{
get { return this.GetProperty(columnName); }
set
{
var error = columnValidations[columnName](value);
if (String.IsNullOrWhiteSpace(error))
errors.Add(error);
else
this.SetProperty(columnName, value);
}
}
}
本文http://www.asp.net/mvc/tutorials/older-versions/models-%28data%29/validating-with-the-idataerrorinfo-interface-cs移动各个验证到的属性:
public partial class Player : IDataErrorInfo
{
Dictionary<string, string> _errorInfo;
public Player()
{
_errorInfo = new Dictionary<string, string>();
}
public bool CanSave { get { return _errorInfo.Count == 0; }
public string this[string columnName]
{
get
{
return _errorInfo.ContainsKey(columnName) ? _errorInfo[columnName] : null;
}
}
public string FirstName
{
get { return _firstName;}
set
{
if (String.IsNullOrWhiteSpace(value))
_errorInfo.AddOrUpdate("FirstName", "Geef een voornaam in");
else
{
_errorInfo.Remove("FirstName");
_firstName = value;
}
}
}
}
(你将不得不处理的字典AddOrUpdate
扩展方法)。 这类似于你的错误计数的想法。
这种方法适用于数据注释。 您也可以在“IsValid的”属性绑定到一个保存按钮启用/禁用。
public abstract class ObservableBase : INotifyPropertyChanged, IDataErrorInfo
{
#region Members
private readonly Dictionary<string, string> errors = new Dictionary<string, string>();
#endregion
#region Events
/// <summary>
/// Property Changed Event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Protected Methods
/// <summary>
/// Get the string name for the property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
var memberExpression = (MemberExpression) expression.Body;
return memberExpression.Member.Name;
}
/// <summary>
/// Notify Property Changed (Shorted method name)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
protected virtual void Notify<T>(Expression<Func<T>> expression)
{
string propertyName = this.GetPropertyName(expression);
PropertyChangedEventHandler handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = this.GetPropertyName(expression);
PropertyChangedEventHandler handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Properties
/// <summary>
/// Gets an error message indicating what is wrong with this object.
/// </summary>
public string Error => null;
/// <summary>
/// Returns true if ... is valid.
/// </summary>
/// <value>
/// <c>true</c> if this instance is valid; otherwise, <c>false</c>.
/// </value>
public bool IsValid => this.errors.Count == 0;
#endregion
#region Indexer
/// <summary>
/// Gets the <see cref="System.String"/> with the specified column name.
/// </summary>
/// <value>
/// The <see cref="System.String"/>.
/// </value>
/// <param name="columnName">Name of the column.</param>
/// <returns></returns>
public string this[string columnName]
{
get
{
var validationResults = new List<ValidationResult>();
string error = null;
if (Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue(this), new ValidationContext(this) { MemberName = columnName }, validationResults))
{
this.errors.Remove(columnName);
}
else
{
error = validationResults.First().ErrorMessage;
if (this.errors.ContainsKey(columnName))
{
this.errors[columnName] = error;
}
else
{
this.errors.Add(columnName, error);
}
}
this.OnPropertyChanged(() => this.IsValid);
return error;
}
}
#endregion
}