PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/src/net40/Radical.Windows.Presentation/AbstractViewModel.cs

#
C# | 392 lines | 149 code | 55 blank | 188 comment | 10 complexity | 47b350301a67c19ca81fc73215965d28 MD5 | raw file
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using Topics.Radical.Linq;
  7. using Topics.Radical.Model;
  8. using Topics.Radical.Validation;
  9. using Topics.Radical.Windows.Presentation.ComponentModel;
  10. using Topics.Radical.Windows.Presentation.Services.Validation;
  11. namespace Topics.Radical.Windows.Presentation
  12. {
  13. /// <summary>
  14. /// A base abstract ViewModel with builtin support for validation, error notification.
  15. /// </summary>
  16. public abstract class AbstractViewModel :
  17. Entity,
  18. IViewModel,
  19. ISupportInitialize
  20. {
  21. /// <summary>
  22. /// Gets or sets the view. The view property is intended only for
  23. /// infrastructural purpose. It is required to hold the one-to-one
  24. /// relation beteewn the view and the view model.
  25. /// </summary>
  26. /// <value>
  27. /// The view.
  28. /// </value>
  29. System.Windows.DependencyObject IViewModel.View { get; set; }
  30. IValidationService _validationService;
  31. /// <summary>
  32. /// Gets the validation service.
  33. /// </summary>
  34. /// <value>The validation service.</value>
  35. protected IValidationService ValidationService
  36. {
  37. get
  38. {
  39. if( this._validationService == null )
  40. {
  41. this._validationService = this.GetValidationService();
  42. this._validationService.StatusChanged += ( s, e ) =>
  43. {
  44. this.ValidationErrors.Clear();
  45. foreach( var error in this._validationService.ValidationErrors )
  46. {
  47. this.ValidationErrors.Add( error );
  48. }
  49. };
  50. this._validationService.Resetted += ( s, e ) =>
  51. {
  52. this.ValidationErrors.Clear();
  53. this.GetType()
  54. .GetProperties()
  55. .Select( p => p.Name )
  56. .ForEach( p => this.OnPropertyChanged( p ) );
  57. };
  58. }
  59. return this._validationService;
  60. }
  61. }
  62. /// <summary>
  63. /// Gets the validation service, this method is called once the first time
  64. /// the validation service is accessed, inheritors should override this method
  65. /// in order to provide a <see cref="IValidationService"/> implementation.
  66. /// </summary>
  67. /// <returns>The validation service to use to validate this view model.</returns>
  68. protected virtual IValidationService GetValidationService()
  69. {
  70. return NullValidationService.Instance;
  71. }
  72. /// <summary>
  73. /// Initializes a new instance of the <see cref="AbstractViewModel"/> class.
  74. /// </summary>
  75. protected AbstractViewModel()
  76. {
  77. this.ValidationErrors = new ObservableCollection<ValidationError>();
  78. }
  79. //protected virtual void OnLoading()
  80. //{
  81. //}
  82. //protected virtual void OnLoaded()
  83. //{
  84. //}
  85. ///// <summary>
  86. ///// Gets or sets a value indicating whether this instance is loaded.
  87. ///// </summary>
  88. ///// <value><c>true</c> if this instance is loaded; otherwise, <c>false</c>.</value>
  89. //protected Boolean IsLoaded
  90. //{
  91. // get;
  92. // private set;
  93. //}
  94. /// <summary>
  95. /// Signals the object that initialization is starting.
  96. /// </summary>
  97. void ISupportInitialize.BeginInit()
  98. {
  99. //this.OnLoading();
  100. }
  101. /// <summary>
  102. /// Signals the object that initialization is complete.
  103. /// </summary>
  104. void ISupportInitialize.EndInit()
  105. {
  106. //this.OnLoaded();
  107. //this.IsLoaded = true;
  108. }
  109. /// <summary>
  110. /// Gets the error.
  111. /// </summary>
  112. /// <value>The error.</value>
  113. /// <remarks>Used only in order to satisfy IDataErrorInfo interface implementation, the default implementation always returns null.</remarks>
  114. public virtual String Error
  115. {
  116. get { return null; }
  117. }
  118. /// <summary>
  119. /// Gets the error message, if any, for the property with the given name.
  120. /// </summary>
  121. public virtual String this[ String propertyName ]
  122. {
  123. get
  124. {
  125. var wasValid = this.IsValid;
  126. var error = this.ValidationService.Validate( propertyName );
  127. if( this.IsValid != wasValid )
  128. {
  129. this.OnPropertyChanged( () => this.IsValid );
  130. }
  131. this.OnValidated();
  132. return error;
  133. }
  134. }
  135. /// <summary>
  136. /// Gets a value indicating whether this instance is valid.
  137. /// </summary>
  138. /// <value><c>true</c> if this instance is valid; otherwise, <c>false</c>.</value>
  139. public virtual Boolean IsValid
  140. {
  141. get { return this.ValidationService.IsValid; }
  142. }
  143. /// <summary>
  144. /// Gets the validation errors if any.
  145. /// </summary>
  146. /// <value>The validation errors.</value>
  147. public virtual ObservableCollection<ValidationError> ValidationErrors
  148. {
  149. get;
  150. private set;
  151. }
  152. /// <summary>
  153. /// Validates this instance.
  154. /// </summary>
  155. /// <returns><c>True</c> if this instance is valid; otherwise <c>false</c>.</returns>
  156. public Boolean Validate()
  157. {
  158. return this.Validate(null, ValidationBehavior.Default );
  159. }
  160. /// <summary>
  161. /// Validates this instance.
  162. /// </summary>
  163. /// <param name="behavior">The behavior.</param>
  164. /// <returns>
  165. /// <c>True</c> if this instance is valid; otherwise <c>false</c>.
  166. /// </returns>
  167. public Boolean Validate( ValidationBehavior behavior )
  168. {
  169. return this.Validate( null, behavior );
  170. }
  171. /// <summary>
  172. /// Validates this instance.
  173. /// </summary>
  174. /// <param name="ruleSet">The rule set.</param>
  175. /// <param name="behavior">The behavior.</param>
  176. /// <returns>
  177. /// <c>True</c> if this instance is valid; otherwise <c>false</c>.
  178. /// </returns>
  179. public virtual Boolean Validate( String ruleSet, ValidationBehavior behavior )
  180. {
  181. this.ValidationService.ValidateRuleSet( ruleSet );
  182. this.OnValidated();
  183. if( behavior == ValidationBehavior.TriggerValidationErrorsOnFailure && !this.ValidationService.IsValid )
  184. {
  185. this.TriggerValidation();
  186. }
  187. return this.ValidationService.IsValid;
  188. }
  189. /// <summary>
  190. /// Occurs when the validation process terminates.
  191. /// </summary>
  192. public event EventHandler Validated;
  193. /// <summary>
  194. /// Raises the Validated event.
  195. /// </summary>
  196. protected virtual void OnValidated()
  197. {
  198. if( this.Validated != null )
  199. {
  200. this.Validated( this, EventArgs.Empty );
  201. }
  202. }
  203. /// <summary>
  204. /// Triggers the validation.
  205. /// </summary>
  206. public virtual void TriggerValidation()
  207. {
  208. if( !this.IsTriggeringValidation )
  209. {
  210. this.IsTriggeringValidation = true;
  211. foreach( var invalid in this.ValidationService.GetInvalidProperties() )
  212. {
  213. this.OnPropertyChanged( invalid );
  214. }
  215. this.IsTriggeringValidation = false;
  216. }
  217. }
  218. /// <summary>
  219. /// Gets or sets a value indicating whether this instance is triggering validation.
  220. /// </summary>
  221. /// <value>
  222. /// <c>true</c> if this instance is triggering validation; otherwise, <c>false</c>.
  223. /// </value>
  224. protected virtual Boolean IsTriggeringValidation
  225. {
  226. get;
  227. private set;
  228. }
  229. /// <summary>
  230. /// Gets or sets the focused element key.
  231. /// </summary>
  232. /// <value>
  233. /// The focused element key.
  234. /// </value>
  235. [MementoPropertyMetadata( TrackChanges = false )]
  236. public String FocusedElementKey
  237. {
  238. get { return this.GetPropertyValue( () => this.FocusedElementKey ); }
  239. set { this.SetPropertyValue( () => this.FocusedElementKey, value ); }
  240. }
  241. /// <summary>
  242. /// Moves the focus to.
  243. /// </summary>
  244. /// <param name="property">The property.</param>
  245. protected virtual void MoveFocusTo<T>( Expression<Func<T>> property )
  246. {
  247. this.EnsureNotDisposed();
  248. var propertyName = property.GetMemberName();
  249. this.MoveFocusTo( propertyName );
  250. }
  251. /// <summary>
  252. /// Moves the focus to.
  253. /// </summary>
  254. /// <param name="focusedElementKey">The focused element key.</param>
  255. protected virtual void MoveFocusTo( String focusedElementKey )
  256. {
  257. this.EnsureNotDisposed();
  258. this.FocusedElementKey = focusedElementKey;
  259. }
  260. }
  261. //class MyColl<T> : ObservableCollection<T>
  262. //{
  263. // class DefferedNotification : IDisposable
  264. // {
  265. // private MyColl<T> myColl;
  266. // public DefferedNotification( MyColl<T> myColl )
  267. // {
  268. // this.myColl = myColl;
  269. // }
  270. // void IDisposable.Dispose()
  271. // {
  272. // myColl.OnCollectionChanged( new System.Collections.Specialized.NotifyCollectionChangedEventArgs( System.Collections.Specialized.NotifyCollectionChangedAction.Add, this.itemsQueue ) );
  273. // foreach( var e in this.propertiesQueue )
  274. // {
  275. // myColl.OnPropertyChanged( e );
  276. // }
  277. // this.ClearQueues();
  278. // this.IsDeferring = false;
  279. // }
  280. // System.Collections.Generic.List<T> itemsQueue = new System.Collections.Generic.List<T>();
  281. // System.Collections.Generic.List<PropertyChangedEventArgs> propertiesQueue = new System.Collections.Generic.List<PropertyChangedEventArgs>();
  282. // internal void AddToNotificationQueue( System.Collections.Generic.IEnumerable<T> range )
  283. // {
  284. // this.itemsQueue.AddRange( range );
  285. // }
  286. // internal void AddToNotificationQueue( PropertyChangedEventArgs e )
  287. // {
  288. // this.propertiesQueue.Add( e );
  289. // }
  290. // void ClearQueues()
  291. // {
  292. // this.itemsQueue.Clear();
  293. // this.propertiesQueue.Clear();
  294. // }
  295. // internal Boolean IsDeferring { get; private set; }
  296. // internal IDisposable StartDefer()
  297. // {
  298. // this.IsDeferring = true;
  299. // return this;
  300. // }
  301. // }
  302. // readonly DefferedNotification defferedNotification = null;
  303. // public MyColl()
  304. // {
  305. // this.defferedNotification = new DefferedNotification( this );
  306. // }
  307. // public void AddRange( System.Collections.Generic.IEnumerable<T> range )
  308. // {
  309. // using( this.defferedNotification.StartDefer() )
  310. // {
  311. // foreach( var t in range )
  312. // {
  313. // this.Add( t );
  314. // }
  315. // this.defferedNotification.AddToNotificationQueue( range );
  316. // }
  317. // }
  318. // protected override void OnPropertyChanged( PropertyChangedEventArgs e )
  319. // {
  320. // if( this.defferedNotification.IsDeferring )
  321. // {
  322. // this.defferedNotification.AddToNotificationQueue( e );
  323. // }
  324. // else
  325. // {
  326. // base.OnPropertyChanged( e );
  327. // }
  328. // }
  329. // protected override void OnCollectionChanged( System.Collections.Specialized.NotifyCollectionChangedEventArgs e )
  330. // {
  331. // if( !this.defferedNotification.IsDeferring )
  332. // {
  333. // base.OnCollectionChanged( e );
  334. // }
  335. // }
  336. //}
  337. }