Given the next example:
Model
public class ProjecteModel : ObservableObject, IProjecteModel
{
private string _patientID
public string IdPacient
{
get => _idPacient;
set
{
_idPacient = value;
OnPropertyChanged(nameof(IdPacient));
}
}
/* More fields here... */
}
Where ObservableObject is a base class that implements INotifyPropertyChanged interface.
ViewModel
public class ProjecteViewModel : BaseViewModel, IDataErrorInfo
{
private IProjecteModel _projecte = new ProjecteModel();
private string _codiClient;
public IProjecteModel Projecte
{
get => _projecte;
set
{
_projecte = value;
OnPropertyChanged(nameof(Projecte));
}
}
public string CodiClient
{
get => _codiClient;
set
{
_codiClient = value;
OnPropertyChanged(nameof(CodiClient));
}
}
public string this[string columnName]
{
get
{
string msg = String.Empty;
switch (columnName)
{
case "CodiClient":
if (CodiClient.Length <= 0)
{
msg = "ID Client is required.";
}
break;
case "IdPacient":
//case "Projecte.IdPacient":
if (Projecte.IdPacient.Length <= 0)
{
msg = "Id Pacient is required.";
}
break;
};
return msg;
}
}
}
View
<!-- Id Pacient -->
<StackPanel Orientation="Vertical"
Width="{Binding ElementName=capComandes, Path=ItemsWidth}">
<TextBlock Text="ID Pacient:"
Style="{StaticResource FieldNameTextBlock}"/>
<TextBox x:Name="IdPacient"
Width="150"
HorizontalAlignment="Left"
Margin="0, 0, 0, 15"
Foreground="{StaticResource ForegroundFieldValuesBrush}"
Text="{Binding Projecte.IdPacient,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"/>
</StackPanel>
<!-- CodiClient -->
<StackPanel Orientation="Vertical"
Width="{Binding ElementName=capComandes, Path=ItemsWidth}">
<TextBlock Text="ID Client:"
Style="{StaticResource FieldNameTextBlock}"/>
<TextBox x:Name="IdClient"
Width="150"
HorizontalAlignment="Left"
Foreground="{StaticResource ForegroundFieldValuesBrush}"
Text="{Binding CodiClient,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}">
</TextBox>
</StackPanel>
Validation works for CodiClient but it doesn't for IdPacient.
Validation method: public string this[string columnName]
is never called for IdPacient.
How can I validate a sub-class property?
CodePudding user response:
IDataErrorInfo
needs to be implemented in whatever class holds the property you are binding to.
Take a look at my blog post, where I have my base bindable object class implement IDataErrorInfo, and include a virtual ValidatePropety()
method, which is called from the public string this[string columnName] {}
property. This is then overridden in each descendent class as required to perform the actual validation on specific properties.
public class perObservableObject: ObservableObject, IDataErrorInfo
{
private HashSet<string> InvalidProperties { get; } = new HashSet<string>();
public virtual bool IsValid => !InvalidProperties.Any();
public bool HasError(string propertyName) => InvalidProperties.Contains(propertyName);
protected virtual string ValidateProperty(string propertyName) => string.Empty;
public string this[string columnName]
{
get
{
if (nameof(IsValid).Equals(columnName))
return string.Empty;
var result = ValidateProperty(columnName);
var errorStateChanged = string.IsNullOrWhiteSpace(result)
? InvalidProperties.Remove(columnName)
: InvalidProperties.Add(columnName);
if (errorStateChanged)
RaisePropertyChanged(nameof(IsValid));
return result;
}
}
// IDataErrorInfo - redundant in WPF
public string Error => string.Empty;
}
[ObservableObject is from MvvmLight]