ObservableCollection对象和ViewModel之间的通信



假设我的ViewModel类中有这个(注意OnSubmit函数(

public class MyViewModel : ViewModelBase{

private string? _SomeName;
public string? SomeName{
get => _SomeName;
set {
Validate(nameof(SomeName)); //Validate data on changes
SetProperty(ref _SomeName, value);
}
}

//Call this function to Validate the input data
private void Validate(string propertyName){
ClearError(propertyName);
switch(propertyName){
case nameof(SomeName):
if (string.IsNullOrWhiteSpace(_SomeName)) 
AddError(nameof(SomeName), "Name cannot be empty.");
break;  
...
//other validation for propeties here if present
}
}

private ObservableCollection<TransactionItem>? _Transactions;
public ObservableCollection<TransactionItem>? Transactions{
get => _CodeItemDisbursement;
set {
SetProperty(ref _Transactions, value); 
}
}

//ICommand Add new item to ObservableCollection
private readonly ButtonCommand _AddItem;
public ICommand AddItem => _AddItem;
private void OnAddItem(object commandParamater){
_Transactions.Add(new TransactionItem(){
... //add the data here...
});
}

//ICommand Form Submission
private readonly ButtonCommand _Submit;
public ICommand Submit => _Submit;
private void OnSubmit(object commandParamater){ //Validate all data before processing it.
//Validate all input is correct before we proceed.
Validate(nameof(SomeName));
// How do I call here, the `Validate()` function 
// of each item in my `ObservableCollection<TransactionItem>`
// and check if `HasErrors` property has some value?

if(HasErrors) return; //There are errors, do not proceed.

//If no errors, then do process the data here...
}

//ViewModel Constructor
public MyViewModel() {
_Submit = new ButtonCommand(OnSubmit);
_AddItem = new ButtonCommand(OnAddItem);
}
}

正如您所看到的,我有一个对象为TransactionItemObservableCollection,这是TransactionItem类。

public class TransactionItem : ViewModelBase{

public int ID { get; set; }
public string? Description { get; set; }
public string? OtherDetails { get; set; }

private string? _TransactionName; 
public string? TransactionName { 
get => _TransactionName; 
set {
Validate(nameof(TransactionName)); //Validate data on changes
SetProperty(ref _TransactionName, value); 
}
}

private decimal _Amount;
public decimal Amount { 
get => _Amount; 
set {
Validate(nameof(Amount)); //Validate data on changes
SetProperty(ref _Amount, value); 
}
}

//Call this function to Validate the input data
private void Validate(string propertyName){
ClearError(propertyName);
switch(propertyName){
case nameof(TransactionName):
if (string.IsNullOrWhiteSpace(_TransactionName)) 
AddError(nameof(TransactionName), "Name of transaction cannot be empty.");
break;
case nameof(Amount):
if (string.IsNullOrWhiteSpace(_Amount.ToString()) || _Amount < 1) 
AddError(nameof(Amount), "Please input a valid amount");
break;  
}
}
}

我还有一个实现INotifyPropertyChangedINotifyDataErrorInfo的类ViewModelBase

public abstract class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo{

#region INotifyPropertyChanged 
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null){
if (EqualityComparer<T>.Default.Equals(member, value)) return false;

member = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
#region INotifyDataErrorInfo | Error Handling
private readonly Dictionary<string, string> _propertyErrors = new();

public bool HasErrors => _propertyErrors.Any();
public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
public void ClearError(string? propertyName){
if (propertyName!=null && _propertyErrors.Remove(propertyName)) OnErrorsChanged(propertyName);
}
public void AddError(string propertyName, string errorMessage){
if (!_propertyErrors.ContainsKey(propertyName)) {
_propertyErrors.Add(propertyName, errorMessage);
}else{ 
_propertyErrors[propertyName] = errorMessage;
}
if (propertyName != null) OnErrorsChanged(propertyName); 
}

private void OnErrorsChanged(string propertyName){
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
public IEnumerable GetErrors(string? propertyName){
if (string.IsNullOrEmpty(propertyName))
yield break;
if (_propertyErrors.TryGetValue(propertyName, out string? existingError))
yield return existingError;
}
#endregion
}

这里发生的事情是,当ViewModel类触发OnSubmit函数时,我想调用Validate()函数来检查是否每个所需的属性都有正确的输入。然后,如果我的ViewModelBase中的HasErrors属性有值,则不要继续处理数据。

问题出在我的ObservableCollection<TransactionItem>中。我还想调用对象TransactionItem中的Validate()函数来验证每个项目的TransactionNameAmount,如果HasErrors有值,则不继续。但我迷路了,我不知道下一步该怎么办。

您可以在TransactionItem中创建一个公共函数来触发Validate()并返回HasErrors的值,然后在OnSubmit()中运行一个循环,并在ObservableCollection<TransactionItem>的每个项中调用该函数。如果其中任何一个返回true,则不要继续。

例如,在TransactionItem中创建一个公共函数

public bool ContainsError(){
Validate(nameof(TransactionName)); //Validate data on changes
Validate(nameof(Amount)); //Validate data on changes
return HasErrors;
}

然后在ViewModel类中,在OnSubmit()函数中执行此

private void OnSubmit(object commandParamater){ 
Validate(nameof(SomeName)); //First, validate your SomeName or other propeties.

bool ContainsError = false;
foreach(TransactionItem item in _Transactions){
ContainsError = item.ContainsError();
}

if(HasErrors || ContainsError) return; //There are errors, do not proceed.

//If no errors, then do process the data here...
}

应该可以。

最新更新