PageRenderTime 37ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/Sample/Sample.ViewModels.WPF+WinRT/ValidationViewModel.cs

#
C# | 78 lines | 59 code | 7 blank | 12 comment | 2 complexity | cd6f68a20e6f4eb275823b698a0d2cef MD5 | raw file
Possible License(s): LGPL-2.0
  1. using System;
  2. using System.ComponentModel.DataAnnotations;
  3. using System.Linq;
  4. using System.Reactive;
  5. using System.Reactive.Linq;
  6. using System.Windows;
  7. using Codeplex.Reactive;
  8. using Codeplex.Reactive.Extensions;
  9. namespace Sample.ViewModels
  10. {
  11. public class ValidationViewModel
  12. {
  13. [Required]
  14. [Range(0, 100)]
  15. public ReactiveProperty<string> ValidationAttr { get; private set; }
  16. public ReactiveProperty<string> ValidationData { get; private set; }
  17. [StringLength(5)]
  18. public ReactiveProperty<string> ValidationBoth { get; private set; }
  19. public ReactiveProperty<string> ErrorInfo { get; private set; }
  20. public ReactiveCommand NextCommand { get; private set; }
  21. public ReactiveProperty<string> AlertMessage { get; private set; }
  22. public ValidationViewModel()
  23. {
  24. // DataAnnotation Attribute, call SetValidateAttribute and select self property
  25. // Note:error result dispatch to IDataErrorInfo, not exception.
  26. // therefore, XAML is ValidatesOnDataErrors=True
  27. ValidationAttr = new ReactiveProperty<string>()
  28. .SetValidateAttribute(() => ValidationAttr);
  29. // IDataErrorInfo, call SetValidateError and set validate condition
  30. // null is success(have no error), string is error message
  31. ValidationData = new ReactiveProperty<string>()
  32. .SetValidateNotifyError((string s) =>
  33. string.IsNullOrEmpty(s) ?
  34. "required" :
  35. s.Cast<char>().All(Char.IsUpper) ?
  36. null :
  37. "not all uppercase");
  38. // Can set both validation
  39. ValidationBoth = new ReactiveProperty<string>()
  40. .SetValidateAttribute(() => ValidationBoth)
  41. .SetValidateNotifyError((string s) => string.IsNullOrEmpty(s) ?
  42. "required" :
  43. s.Cast<char>().All(Char.IsLower) ?
  44. null :
  45. "not all lowercase");
  46. // Validation result is pushed to ObserveErrorChanged
  47. var errors = Observable.Merge(
  48. ValidationAttr.ObserveErrorChanged,
  49. ValidationData.ObserveErrorChanged,
  50. ValidationBoth.ObserveErrorChanged);
  51. // Use OfType, choose error source
  52. ErrorInfo = Observable.Merge(
  53. errors.Where(o => o == null).Select(_ => ""), // success
  54. errors.OfType<Exception>().Select(e => e.Message), // from attribute
  55. errors.OfType<string>()) // from IDataErrorInfo
  56. .ToReactiveProperty();
  57. // Validation is view initialized not run in default.
  58. // If want to validate on view initialize,
  59. // use ReactivePropertyMode.RaiseLatestValueOnSubscribe to ReactiveProperty
  60. // that mode is validate values on initialize.
  61. NextCommand = ValidationAttr.ObserveErrorChanged
  62. .CombineLatest(
  63. ValidationData.ObserveErrorChanged,
  64. ValidationBoth.ObserveErrorChanged,
  65. (a, b, c) => new[] { a, b, c }.All(x => x == null))
  66. .ToReactiveCommand(initialValue: false);
  67. this.AlertMessage = this.NextCommand.Select(_ => "Can go to next!")
  68. .ToReactiveProperty(mode: ReactivePropertyMode.None);
  69. }
  70. }
  71. }