Add some kind of [TrySetProperty] attribute
Overview
The problem I would like to solve is: Don't change a value when it fails validation but instead invoke the ErrorsChanged event, all of this just with attributes / code generators.
More in detail: As soon as you don't want invalid values to be saved you can no longer work with simple attributes but have to use TrySetProperty. But TrySetProperty is missing an overload to invoke the ErrorsChanged event but only relies on the out parameter to deliver validation errors. Therefore any event based validation handling stops working and needs some kind of workaround (like firing additional OnPropertyChanged calls to process the validation errors or to manually call ValidateProperty again just so that the ErrorsChanged event gets invoked.
API breakdown
namespace CommunityToolkit.Mvvm.ComponentModel;
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public sealed class TrySetProperty : Attribute
{
}
Usage example
[ObervableObject]
[TrySetProperty]
[MaxLength(20)]
private string name;
which would generate something like
private string name;
[MaxLength(20)]
public string Name
{
get => name;
set => TrySetProperty(ref name, value); // <- using a new overload that works with ErrorsChanged the same way as SetProperty(ref name, value, true) does.
Breaking change?
I'm not sure
Alternatives
Currently I only see one workaround if event based validation handling is used, doing an additional, unneeded validation just to trigger the event:
private string name;
[MaxLength(20)]
public string Name
{
get => name;
set
{
if (!TrySetProperty(ref name, value, out _)) ValidateProperty(nameof(Name));
}
Every other workaround ignores events and just processes the provided IReadOnlyCollection<ValidationResult> out variable.
Additional context
No response
Help us help you
No, just wanted to propose this
There's a problem with this, however. You're going to run into issues with FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty (docs)
This is commonly seen in WPF when binding a TextBox to a double (for example, see this Stack Overflow post)
Suppose:
- you have a
TextBox, where the user is supposed to enter an IPv4 address (e.g.,10.20.30.40) - That
TextBoxhas anUpdateSourceTriggerofPropertyChanged - The user is using WPF in .NET Core, .NET 5 or later, or .NET Framework 4.5 or later
- The developer has not disabled
FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty
The user types a 1 in the TextBox.
- The binding engine sends
1back to the source (your view model'sPathproperty) - The
TrySetPropertymethod conducts validation - it fails, because1is not a valid IP address - The
TrySetPropertymethod does not propagate the change to the field - The
TextBox's display does not accept the1 - The
TextBoxremains empty - The user can never type a valid IP address one keystroke at a time - because the intermediate steps are invalid
In cases like these, your view model should represent the "working state", not the final "approved state".