LoginView.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using System.Reactive.Disposables;
  2. using System.Reactive.Linq;
  3. using NStack;
  4. using ReactiveUI;
  5. using Terminal.Gui;
  6. namespace ReactiveExample {
  7. public class LoginView : Window, IViewFor<LoginViewModel> {
  8. readonly CompositeDisposable _disposable = new CompositeDisposable();
  9. public LoginView (LoginViewModel viewModel) : base("Reactive Extensions Example") {
  10. ViewModel = viewModel;
  11. var title = TitleLabel ();
  12. var usernameLengthLabel = UsernameLengthLabel (title);
  13. var usernameInput = UsernameInput (usernameLengthLabel);
  14. var passwordLengthLabel = PasswordLengthLabel (usernameInput);
  15. var passwordInput = PasswordInput (passwordLengthLabel);
  16. var validationLabel = ValidationLabel (passwordInput);
  17. var loginButton = LoginButton (validationLabel);
  18. var clearButton = ClearButton (loginButton);
  19. LoginProgressLabel (clearButton);
  20. }
  21. public LoginViewModel ViewModel { get; set; }
  22. protected override void Dispose (bool disposing) {
  23. _disposable.Dispose ();
  24. base.Dispose (disposing);
  25. }
  26. Label TitleLabel () {
  27. var label = new Label("Login Form");
  28. Add (label);
  29. return label;
  30. }
  31. TextField UsernameInput (View previous) {
  32. var usernameInput = new TextField (ViewModel.Username) {
  33. X = Pos.Left(previous),
  34. Y = Pos.Top(previous) + 1,
  35. Width = 40
  36. };
  37. ViewModel
  38. .WhenAnyValue (x => x.Username)
  39. .BindTo (usernameInput, x => x.Text)
  40. .DisposeWith (_disposable);
  41. usernameInput
  42. .Events ()
  43. .TextChanged
  44. .Select (old => usernameInput.Text)
  45. .DistinctUntilChanged ()
  46. .BindTo (ViewModel, x => x.Username)
  47. .DisposeWith (_disposable);
  48. Add (usernameInput);
  49. return usernameInput;
  50. }
  51. Label UsernameLengthLabel (View previous) {
  52. var usernameLengthLabel = new Label {
  53. X = Pos.Left(previous),
  54. Y = Pos.Top(previous) + 1,
  55. Width = 40
  56. };
  57. ViewModel
  58. .WhenAnyValue (x => x.UsernameLength)
  59. .Select (length => ustring.Make ($"Username ({length} characters)"))
  60. .BindTo (usernameLengthLabel, x => x.Text)
  61. .DisposeWith (_disposable);
  62. Add (usernameLengthLabel);
  63. return usernameLengthLabel;
  64. }
  65. TextField PasswordInput (View previous) {
  66. var passwordInput = new TextField (ViewModel.Password) {
  67. X = Pos.Left(previous),
  68. Y = Pos.Top(previous) + 1,
  69. Width = 40
  70. };
  71. ViewModel
  72. .WhenAnyValue (x => x.Password)
  73. .BindTo (passwordInput, x => x.Text)
  74. .DisposeWith (_disposable);
  75. passwordInput
  76. .Events ()
  77. .TextChanged
  78. .Select (old => passwordInput.Text)
  79. .DistinctUntilChanged ()
  80. .BindTo (ViewModel, x => x.Password)
  81. .DisposeWith (_disposable);
  82. Add (passwordInput);
  83. return passwordInput;
  84. }
  85. Label PasswordLengthLabel (View previous) {
  86. var passwordLengthLabel = new Label {
  87. X = Pos.Left(previous),
  88. Y = Pos.Top(previous) + 1,
  89. Width = 40
  90. };
  91. ViewModel
  92. .WhenAnyValue (x => x.PasswordLength)
  93. .Select (length => ustring.Make ($"Password ({length} characters)"))
  94. .BindTo (passwordLengthLabel, x => x.Text)
  95. .DisposeWith (_disposable);
  96. Add (passwordLengthLabel);
  97. return passwordLengthLabel;
  98. }
  99. Label ValidationLabel (View previous) {
  100. var error = ustring.Make("Please, enter user name and password.");
  101. var success = ustring.Make("The input is valid!");
  102. var validationLabel = new Label(error) {
  103. X = Pos.Left(previous),
  104. Y = Pos.Top(previous) + 1,
  105. Width = 40
  106. };
  107. ViewModel
  108. .WhenAnyValue (x => x.IsValid)
  109. .Select (valid => valid ? success : error)
  110. .BindTo (validationLabel, x => x.Text)
  111. .DisposeWith (_disposable);
  112. ViewModel
  113. .WhenAnyValue (x => x.IsValid)
  114. .Select (valid => valid ? Colors.Base : Colors.Error)
  115. .BindTo (validationLabel, x => x.ColorScheme)
  116. .DisposeWith (_disposable);
  117. Add (validationLabel);
  118. return validationLabel;
  119. }
  120. Label LoginProgressLabel (View previous) {
  121. var progress = ustring.Make ("Logging in...");
  122. var idle = ustring.Make ("Press 'Login' to log in.");
  123. var loginProgressLabel = new Label(idle) {
  124. X = Pos.Left(previous),
  125. Y = Pos.Top(previous) + 1,
  126. Width = 40
  127. };
  128. ViewModel
  129. .WhenAnyObservable (x => x.Login.IsExecuting)
  130. .Select (executing => executing ? progress : idle)
  131. .ObserveOn (RxApp.MainThreadScheduler)
  132. .BindTo (loginProgressLabel, x => x.Text)
  133. .DisposeWith (_disposable);
  134. Add (loginProgressLabel);
  135. return loginProgressLabel;
  136. }
  137. Button LoginButton (View previous) {
  138. var loginButton = new Button ("Login") {
  139. X = Pos.Left(previous),
  140. Y = Pos.Top(previous) + 1,
  141. Width = 40
  142. };
  143. loginButton
  144. .Events ()
  145. .Clicked
  146. .InvokeCommand (ViewModel, x => x.Login)
  147. .DisposeWith (_disposable);
  148. Add (loginButton);
  149. return loginButton;
  150. }
  151. Button ClearButton (View previous) {
  152. var clearButton = new Button("Clear") {
  153. X = Pos.Left(previous),
  154. Y = Pos.Top(previous) + 1,
  155. Width = 40
  156. };
  157. clearButton
  158. .Events ()
  159. .Clicked
  160. .InvokeCommand (ViewModel, x => x.Clear)
  161. .DisposeWith (_disposable);
  162. Add (clearButton);
  163. return clearButton;
  164. }
  165. object IViewFor.ViewModel {
  166. get => ViewModel;
  167. set => ViewModel = (LoginViewModel) value;
  168. }
  169. }
  170. }