在我的ViewModel中,我实现了IDataErrorInfo接口(以及INotifyPropertyChanged)。输入验证按预期工作,我没有问题。
我将此属性作为IDataErrorInfo的一部分public string Error { get { return this[null]; } }
据我所知,如果所有经验证的输入都通过验证,则Error
应为空,因此我将其作为我的CanExecute方法传递
return !string.IsNullOrEmpty(Error);
但是,我的“保存”按钮永远不会启用。我的理由是CanExecuteChanged
永远不会被惹恼。如果这是真的,我应该在哪里以及如何触发它?
这是我的RelayCommand类。我尝试过其他方法,但结果是一样的。我认为它有效,因为如果我没有将CanExecute方法传递给构造函数,则启用“保存”按钮。
public class RelayCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { execute(); }
}
“保存”按钮:
<Button Content="Save" Command="{Binding InsertCommand}"/>
的InsertCommand:
public RelayCommand InsertCommand { get; internal set; }
在ViewModel构造函数中:
InsertCommand = new RelayCommand(ExecuteInsert, CanExecuteInsert);
CanExecute:
bool CanExecuteInsert()
{
return !string.IsNullOrEmpty(Error);
}
答案 0 :(得分:4)
您还没有为我们添加足够的代码来准确地告诉您问题所在。但是,您正在采取正确的方法。我也使用IDataErrorInfo
接口,但我在实现它的基类中添加了一些额外的属性:
public string Error // actual IDataErrorInfo Member
{
get
{
if (!HasError) return string.Empty;
StringBuilder errors = new StringBuilder();
foreach (string error in Errors) errors.AppendUniqueOnNewLineIfNotEmpty(error);
return errors.ToString();
}
}
public virtual ObservableCollection<string> Errors
{
get { return errors; }
}
public virtual bool HasError
{
get { return Errors != null && Errors.Count > 0; }
}
Errors
集合只允许我同时维护多个错误,HasError
只是告诉我是否有任何错误。 Errors
集合使用每种数据类型中的IDataErrorInfo
索引器填充:
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Title"]);
errors.AddUniqueIfNotEmpty(this["Artist"]);
...
errors.AddUniqueIfNotEmpty(this["DealerPrice"]);
return errors;
}
}
因此,为了回答您的实际问题,我会像CanExecute
那样处理Save Command
的{{1}}功能:
public override ICommand Save
{
get { return new ActionCommand(action => SaveCommand(), canExecute =>
CanSave(DigitalServiceProviderPriceTier)); }
}
...
private bool CanSave(DigitalServiceProviderPriceTier digitalServiceProviderPriceTier)
{
return digitalServiceProviderPriceTier != null &&
digitalServiceProviderPriceTier.HasChanges &&
!digitalServiceProviderPriceTier.HasError; // <-- Important part
}
所以,似乎你在几乎中以同样的方式这样做 - 我的附加属性当然是可选的。如果您的Error
属性永远不会为空,那么我会说 是你的问题。首先调试它,然后看看它实际上有什么价值......也许总有一个不应该出现的错误?
啊......我刚刚注意到你的Error
属性代码...... 是你的问题:
public string Error { get { return this[null]; } }
您正在使用值null
调用索引器,因此索引器中返回的代码实际上是您的Error
属性值。如果没有验证错误,索引器也应该返回一个空字符串:
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "SomePropertyName" && SomePropertyName.IsNullOrEmpty())
error = "You must enter some property.";
if (propertyName == "OtherPropertyName" && OtherPropertyName.Length != 3)
error = "The OtherPropertyName must be 3 characters long.";
...
return error;
}
}
然后在Error
属性中,您应该像我在Errors
属性中那样调用要验证的实际属性名称而不是 null
。因此,在上面的示例中,您可以在Error
属性中调用类似的内容:
string error = this["SomePropertyName"];
if (error == string.Empty) error = this["OtherPropertyName"];
return error;
再一次,我写了太多的信息......我只是希望这一切都对你有意义,你不会回答几十个新问题。希望它足够清楚。