@@ -24,18 +24,28 @@ public abstract class ObservableValidator : ObservableObject, INotifyDataErrorIn
24
24
/// </summary>
25
25
private readonly Dictionary < string , List < ValidationResult > > errors = new Dictionary < string , List < ValidationResult > > ( ) ;
26
26
27
- /// <summary>
28
- /// Indicates the total number of properties with errors (not total errors).
29
- /// This is used to allow <see cref="HasErrors"/> to operate in O(1) time, as it can just
30
- /// check whether this value is not 0 instead of having to traverse <see cref="errors"/>.
31
- /// </summary>
32
- private int totalErrors ;
33
-
34
27
/// <inheritdoc/>
35
28
public event EventHandler < DataErrorsChangedEventArgs > ? ErrorsChanged ;
36
29
37
30
/// <inheritdoc/>
38
- public bool HasErrors => this . totalErrors > 0 ;
31
+ public bool HasErrors
32
+ {
33
+ get
34
+ {
35
+ // This uses the value enumerator for Dictionary<TKey, TValue>.ValueCollection, so it doesn't
36
+ // allocate. Accessing this property is O(n), but we can stop as soon as we find at least one
37
+ // error in the whole entity, and doing this saves 8 bytes in the object size (no fields needed).
38
+ foreach ( var value in this . errors . Values )
39
+ {
40
+ if ( value . Count > 0 )
41
+ {
42
+ return true ;
43
+ }
44
+ }
45
+
46
+ return false ;
47
+ }
48
+ }
39
49
40
50
/// <summary>
41
51
/// Compares the current and new values for a given property. If the value has changed,
@@ -273,28 +283,10 @@ private void ValidateProperty(object? value, string? propertyName)
273
283
new ValidationContext ( this , null , null ) { MemberName = propertyName } ,
274
284
propertyErrors ) ;
275
285
276
- // Update the state and/or the errors for the property
277
- if ( isValid )
278
- {
279
- if ( errorsChanged )
280
- {
281
- this . totalErrors -- ;
282
- }
283
- }
284
- else
285
- {
286
- if ( ! errorsChanged )
287
- {
288
- this . totalErrors ++ ;
289
- }
290
-
291
- errorsChanged = true ;
292
- }
293
-
294
286
// Only raise the event once if needed. This happens either when the target property
295
287
// had existing errors and is now valid, or if the validation has failed and there are
296
288
// new errors to broadcast, regardless of the previous validation state for the property.
297
- if ( errorsChanged )
289
+ if ( errorsChanged || ! isValid )
298
290
{
299
291
ErrorsChanged ? . Invoke ( this , new DataErrorsChangedEventArgs ( propertyName ) ) ;
300
292
}
0 commit comments