Reputation: 3537
Let's say I have this in my ViewModel
class. (Notice the OnSubmit
function)
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);
}
}
As you can see, I have an ObservableCollection
with an object of TransactionItem
, and here is the TransactionItem
class.
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;
}
}
}
I also have a class of ViewModelBase
that implements the INotifyPropertyChanged
, and INotifyDataErrorInfo
.
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
}
What happened here is that, when the OnSubmit
function is triggered from the ViewModel
class, I want to call the Validate()
function to check if every required property has the correct input. Then if the HasErrors
property from my ViewModelBase
has a value, then do not proceed to process the data.
The problem is in my ObservableCollection<TransactionItem>
. I also want to call the Validate()
function in the object TransactionItem
to validate the TransactionName
, and Amount
of each item and not proceed if HasErrors
has a value. But I'm lost and I don't know what should I do next.
Upvotes: 0
Views: 74
Reputation: 3537
You could make a public function in your TransactionItem
that triggers the Validate()
and returns the value of HasErrors
, then run a loop in your OnSubmit()
and call that function in every item of your ObservableCollection<TransactionItem>
. If any of it returns true, then do not proceed.
For example, create a public function in your TransactionItem
public bool ContainsError(){
Validate(nameof(TransactionName)); //Validate data on changes
Validate(nameof(Amount)); //Validate data on changes
return HasErrors;
}
Then in your ViewModel
class, in OnSubmit()
function do this
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...
}
That should do.
Upvotes: 1