1+ using System ;
2+ using System . Collections ;
3+ using System . Collections . Generic ;
4+ using System . ComponentModel ;
5+ using System . ComponentModel . DataAnnotations ;
6+ using System . Linq ;
7+ using System . Reflection ;
8+ using System . Runtime . CompilerServices ;
9+
10+ using ValidationResult = System . ComponentModel . DataAnnotations . ValidationResult ;
11+
12+ namespace RemoteDesktop . Common . Base ;
13+
14+ /// <summary>
15+ /// Extends BaseViewModel with support for data validation using DataAnnotations.
16+ /// </summary>
17+ internal abstract class ValidatableViewModel : BaseViewModel , INotifyDataErrorInfo
18+ {
19+ private readonly IDictionary < string , IList < string > > _errors = new Dictionary < string , IList < string > > ( ) ;
20+
21+ /// <summary>
22+ /// Event raised when the validation errors for a property have changed.
23+ /// </summary>
24+ public event EventHandler < DataErrorsChangedEventArgs > ErrorsChanged ;
25+
26+ /// <summary>
27+ /// Indicates whether the object has any validation errors.
28+ /// </summary>
29+ public bool HasErrors => _errors . Any ( ) ;
30+
31+ /// <summary>
32+ /// Gets the validation errors for a specific property.
33+ /// </summary>
34+ public IEnumerable GetErrors ( string propertyName )
35+ {
36+ if ( string . IsNullOrEmpty ( propertyName ) )
37+ {
38+ return _errors . SelectMany ( e => e . Value ) ;
39+ }
40+
41+ return _errors . TryGetValue ( propertyName , out var errors ) ? errors : Enumerable . Empty < string > ( ) ;
42+ }
43+
44+ /// <summary>
45+ /// Raises PropertyChanged and validates the changed property.
46+ /// </summary>
47+ protected override void OnPropertyChanged ( [ CallerMemberName ] string propertyName = null )
48+ {
49+ base . OnPropertyChanged ( propertyName ) ;
50+ ValidateProperty ( propertyName ) ;
51+ }
52+
53+ /// <summary>
54+ /// Validates a single property using DataAnnotation attributes.
55+ /// Updates the internal error collection and raises ErrorsChanged if necessary.
56+ /// </summary>
57+ protected virtual void ValidateProperty ( string propertyName )
58+ {
59+ if ( string . IsNullOrEmpty ( propertyName ) )
60+ {
61+ return ;
62+ }
63+
64+ var propertyInfo = GetType ( ) . GetProperty ( propertyName ) ;
65+ if ( propertyInfo == null )
66+ {
67+ return ;
68+ }
69+
70+ var value = propertyInfo . GetValue ( this ) ;
71+ var context = new ValidationContext ( this )
72+ {
73+ MemberName = propertyName
74+ } ;
75+
76+ var results = new List < ValidationResult > ( ) ;
77+ Validator . TryValidateProperty ( value , context , results ) ;
78+
79+ if ( _errors . ContainsKey ( propertyName ) )
80+ {
81+ _errors . Remove ( propertyName ) ;
82+ }
83+
84+ if ( results . Any ( ) )
85+ {
86+ _errors [ propertyName ] = [ .. results . Select ( r => r . ErrorMessage ) ] ;
87+ }
88+
89+ ErrorsChanged ? . Invoke ( this , new DataErrorsChangedEventArgs ( propertyName ) ) ;
90+ }
91+
92+ /// <summary>
93+ /// Validates all public properties of the object.
94+ /// Useful when submitting a form or performing global checks.
95+ /// </summary>
96+ public void ValidateAllProperties ( )
97+ {
98+ foreach ( var property in GetType ( ) . GetProperties ( BindingFlags . Public | BindingFlags . Instance ) )
99+ {
100+ ValidateProperty ( property . Name ) ;
101+ }
102+ }
103+ }
0 commit comments