CommunityToolkit / CommunityToolkit/dotnet
Implements NotifyPropertyValidationForAttribute
- Dominant language
- C#
- Stars
- 3.8k
- Forks
- 400
- PR merge metrics
- No merged PRs in 30d
Description
### Overview
Somes read-only properties may dependes on other to be validated, when the source property change, we shall be able to trigger validation in to a target property that dependes on it.
So a `[NotifyPropertyValidationFor(nameof(TargetProperty))] ` can be used to annotate the source property and then generate the code to call `ValidateProperty(TargetProperty, nameof(TargetProperty))`;
### API breakdown
Notify property change attribute used to decorate validation dependencies.
```csharp
//
/// An attribute that can be used to validate the target property when te annotated property changes. When this attribute is
/// used, the generated property setter will also call for the properties specified in the attribute data. This can be useful to keep the code compact when
/// there are one or more dependent properties that should be validated when the value of the annotated observable
/// property is changed. If this attribute is used in a field without , it is ignored.
///
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
public class NotifyPropertyValidationForAttribute : Attribute
{
///
/// Initializes a new instance of the class.
///
/// The name of the property to validate when the annotated property changes.
public NotifyPropertyValidationForAttribute(string propertyName);
///
/// Initializes a new instance of the class.
///
/// The name of the property validate when the annotated property changes.
///
/// The other property names to validate when the annotated property changes. This parameter can optionally
/// be used to indicate a series of dependent properties from the same attribute, to keep the code more compact.
///
public NotifyPropertyValidationForAttribute(string propertyName, params string[] otherPropertyNames);
///
/// Gets the property names to validate when the annotated property changes.
///
public string[] PropertyNames { get; }
}
```
The Source Generator for properties shall include the call to ValidateProperty after notify the change and only when the property has changed like this.
```csharp
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public int Name
{
get => _name;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(_name, value))
{
OnNameChanging(value);
OnNameChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
_name = value;
ValidateProperty(value, "SelectedBirthMonthIndex");
OnNameChanged(value);
OnNameChanged(default, value);
//Notify validation dependency property here
ValidateProperty(FullName, "FullName")
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.FullName);
}
}
}
```
### Usage example
In the next sample the FullName property depends on _name and _lastName and as a computed property validation needs to be fired externally, currently we can do this using the partials method on each source property but annotate the property with `NotifyPropertyValidationFor` will be easier.
```csharp
public class MyClass : ObservableValidator
{
[Required]
[ObservableProperty]
[NotifyPropertyValidationFor(nameof(FullName))]
private string _name;
[Required[
[ObservableProperty]
[NotifyPropertyValidationFor(nameof(FullName))]
private string _lastName;
[MinLength(5)]
[MaxLength(100)]
[CustomValidation(typeof(MyClass), nameof(ValidateFullName))]
public string FullName => $"{Name} {LastName}";
public static ValidationResult ValidateFullName(string name, ValidationContext context)
{
MyClass instance = (MyClass)context.ObjectInstance;
bool isValid = CustonValidationHere(FullName);
if (isValid)
{
return ValidationResult.Success;
}
return new("The name was not validated by the fancy custom validator");
}
}
```
### Breaking change?
I'm not sure
### Alternatives
As mention before the current alternative to implement the `OnPropertyChanged` partial method for each source property and call to `ValidateProperty(TargetProperty, nameof(TargetProperty));`
Like this:
```csharp
public class MyClass : ObservableValidator
{
[Required]
[ObservableProperty]
private string _name;
[Required[
[ObservableProperty]
private string _lastName;
[MinLength(5)]
[MaxLength(100)]
[CustomValidation(typeof(MyClass), nameof(ValidateFullName))]
public string FullName => $"{Name} {LastName}";
public static ValidationResult ValidateFullName(string name, ValidationContext context)
{
MyClass instance = (MyClass)context.ObjectInstance;
bool isValid = CustonValidationHere(FullName);
if (isValid)
{
return ValidationResult.Success;
}
return new("The name was not validated by the fancy custom validator");
}
partial void OnNameChanged(string value)
{
ValidateProperty(FullName, nameof(FullName));
}
partial void OnLastNameChanged(string value)
{
ValidateProperty(FullName, nameof(FullName));
}
}
```
### Additional context
_No response_
### Help us help you
Yes, I'd like to be assigned to work on this item
Contributor guide
Assessment
This issue has not been assessed yet.