77using System . ComponentModel ;
88using System . ComponentModel . DataAnnotations ;
99using System . Linq ;
10+ using System . Reflection ;
1011using System . Text . RegularExpressions ;
1112using Microsoft . Toolkit . Mvvm . ComponentModel ;
1213using Microsoft . VisualStudio . TestTools . UnitTesting ;
@@ -335,6 +336,54 @@ public void Test_ObservableValidator_ValidateAllProperties()
335336 model . Age = - 10 ;
336337
337338 model . ValidateAllProperties ( ) ;
339+
340+ Assert . IsTrue ( model . HasErrors ) ;
341+ Assert . IsTrue ( events . Count == 1 ) ;
342+ Assert . IsTrue ( events . Any ( e => e . PropertyName == nameof ( Person . Age ) ) ) ;
343+ }
344+
345+ [ TestCategory ( "Mvvm" ) ]
346+ [ TestMethod ]
347+ public void Test_ObservableValidator_ValidateAllProperties_WithFallback ( )
348+ {
349+ var model = new PersonWithDeferredValidation ( ) ;
350+ var events = new List < DataErrorsChangedEventArgs > ( ) ;
351+
352+ MethodInfo [ ] staticMethods = typeof ( ObservableValidator ) . GetMethods ( BindingFlags . Static | BindingFlags . NonPublic ) ;
353+ MethodInfo validationMethod = staticMethods . Single ( static m => m . Name . Contains ( "GetValidationActionFallback" ) ) ;
354+ Func < Type , Action < object > > validationFunc = ( Func < Type , Action < object > > ) validationMethod . CreateDelegate ( typeof ( Func < Type , Action < object > > ) ) ;
355+ Action < object > validationAction = validationFunc ( model . GetType ( ) ) ;
356+
357+ model . ErrorsChanged += ( s , e ) => events . Add ( e ) ;
358+
359+ validationAction ( model ) ;
360+
361+ Assert . IsTrue ( model . HasErrors ) ;
362+ Assert . IsTrue ( events . Count == 2 ) ;
363+
364+ // Note: we can't use an index here because the order used to return properties
365+ // from reflection APIs is an implementation detail and might change at any time.
366+ Assert . IsTrue ( events . Any ( e => e . PropertyName == nameof ( Person . Name ) ) ) ;
367+ Assert . IsTrue ( events . Any ( e => e . PropertyName == nameof ( Person . Age ) ) ) ;
368+
369+ events . Clear ( ) ;
370+
371+ model . Name = "James" ;
372+ model . Age = 42 ;
373+
374+ validationAction ( model ) ;
375+
376+ Assert . IsFalse ( model . HasErrors ) ;
377+ Assert . IsTrue ( events . Count == 2 ) ;
378+ Assert . IsTrue ( events . Any ( e => e . PropertyName == nameof ( Person . Name ) ) ) ;
379+ Assert . IsTrue ( events . Any ( e => e . PropertyName == nameof ( Person . Age ) ) ) ;
380+
381+ events . Clear ( ) ;
382+
383+ model . Age = - 10 ;
384+
385+ validationAction ( model ) ;
386+
338387 Assert . IsTrue ( model . HasErrors ) ;
339388 Assert . IsTrue ( events . Count == 1 ) ;
340389 Assert . IsTrue ( events . Any ( e => e . PropertyName == nameof ( Person . Age ) ) ) ;
@@ -414,6 +463,34 @@ public void Test_ObservableValidator_ValidationWithFormattedDisplayName()
414463 Assert . AreEqual ( allErrors [ 1 ] . ErrorMessage , $ "SECOND: { nameof ( ValidationWithDisplayName . AnotherRequiredField ) } .") ;
415464 }
416465
466+ // See: https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4272
467+ [ TestCategory ( "Mvvm" ) ]
468+ [ TestMethod ]
469+ [ DataRow ( typeof ( MyBase ) ) ]
470+ [ DataRow ( typeof ( MyDerived2 ) ) ]
471+ public void Test_ObservableRecipient_ValidationOnNonValidatableProperties ( Type type )
472+ {
473+ MyBase viewmodel = ( MyBase ) Activator . CreateInstance ( type ) ;
474+
475+ viewmodel . ValidateAll ( ) ;
476+ }
477+
478+ // See: https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4272
479+ [ TestCategory ( "Mvvm" ) ]
480+ [ TestMethod ]
481+ [ DataRow ( typeof ( MyBase ) ) ]
482+ [ DataRow ( typeof ( MyDerived2 ) ) ]
483+ public void Test_ObservableRecipient_ValidationOnNonValidatableProperties_WithFallback ( Type type )
484+ {
485+ MyBase viewmodel = ( MyBase ) Activator . CreateInstance ( type ) ;
486+
487+ MethodInfo [ ] staticMethods = typeof ( ObservableValidator ) . GetMethods ( BindingFlags . Static | BindingFlags . NonPublic ) ;
488+ MethodInfo validationMethod = staticMethods . Single ( static m => m . Name . Contains ( "GetValidationActionFallback" ) ) ;
489+ Func < Type , Action < object > > validationFunc = ( Func < Type , Action < object > > ) validationMethod . CreateDelegate ( typeof ( Func < Type , Action < object > > ) ) ;
490+
491+ validationFunc ( viewmodel . GetType ( ) ) ( viewmodel ) ;
492+ }
493+
417494 public class Person : ObservableValidator
418495 {
419496 private string name ;
@@ -631,5 +708,22 @@ public string AnotherRequiredField
631708 set => SetProperty ( ref this . anotherRequiredField , value , true ) ;
632709 }
633710 }
711+
712+ public class MyBase : ObservableValidator
713+ {
714+ public int ? MyDummyInt { get ; set ; } = 0 ;
715+
716+ public void ValidateAll ( )
717+ {
718+ ValidateAllProperties ( ) ;
719+ }
720+ }
721+
722+ public class MyDerived2 : MyBase
723+ {
724+ public string Name { get ; set ; }
725+
726+ public int SomeRandomproperty { get ; set ; }
727+ }
634728 }
635729}
0 commit comments