Add documentation

This commit is contained in:
Artyom
2020-10-01 12:45:38 +03:00
parent e5b0795aff
commit 7b199e8379
5 changed files with 65 additions and 7 deletions

View File

@@ -17,6 +17,7 @@ namespace ReactiveExample {
.Append (PasswordInput ())
.Append (ValidationLabel ())
.Append (LoginButton ())
.Append (ClearButton ())
.Append (LoginProgressLabel ());
}
@@ -36,7 +37,7 @@ namespace ReactiveExample {
usernameInput
.Events ()
.TextChanged
.Select (old => usernameInput.Text.ToString ())
.Select (old => usernameInput.Text)
.DistinctUntilChanged ()
.BindTo (ViewModel, x => x.Username)
.DisposeWith (_disposable);
@@ -62,7 +63,7 @@ namespace ReactiveExample {
passwordInput
.Events ()
.TextChanged
.Select (old => passwordInput.Text.ToString ())
.Select (old => passwordInput.Text)
.DistinctUntilChanged ()
.BindTo (ViewModel, x => x.Password)
.DisposeWith (_disposable);
@@ -117,6 +118,16 @@ namespace ReactiveExample {
.DisposeWith (_disposable);
return loginButton;
}
Button ClearButton () {
var clearButton = new Button("Clear") { Width = 40 };
clearButton
.Events ()
.Clicked
.InvokeCommand (ViewModel, x => x.Clear)
.DisposeWith (_disposable);
return clearButton;
}
object IViewFor.ViewModel {
get => ViewModel;

View File

@@ -1,8 +1,10 @@
using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using NStack;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -18,8 +20,8 @@ namespace ReactiveExample {
x => x.Username,
x => x.Password,
(username, password) =>
!string.IsNullOrWhiteSpace (username) &&
!string.IsNullOrWhiteSpace (password));
!ustring.IsNullOrEmpty (username) &&
!ustring.IsNullOrEmpty (password));
_isValid = canLogin.ToProperty (this, x => x.IsValid);
Login = ReactiveCommand.CreateFromTask (
@@ -34,13 +36,19 @@ namespace ReactiveExample {
.WhenAnyValue (x => x.Password)
.Select (password => password.Length)
.ToProperty (this, x => x.PasswordLength);
Clear = ReactiveCommand.Create (() => { });
Clear.Subscribe (unit => {
Username = ustring.Empty;
Password = ustring.Empty;
});
}
[Reactive, DataMember]
public string Username { get; set; } = string.Empty;
public ustring Username { get; set; } = ustring.Empty;
[Reactive, DataMember]
public string Password { get; set; } = string.Empty;
public ustring Password { get; set; } = ustring.Empty;
[IgnoreDataMember]
public int UsernameLength => _usernameLength.Value;
@@ -51,6 +59,9 @@ namespace ReactiveExample {
[IgnoreDataMember]
public ReactiveCommand<Unit, Unit> Login { get; }
[IgnoreDataMember]
public ReactiveCommand<Unit, Unit> Clear { get; }
[IgnoreDataMember]
public bool IsValid => _isValid.Value;
}

View File

@@ -5,7 +5,7 @@ namespace ReactiveExample {
public static class Program {
static void Main (string [] args) {
Application.Init (); // A hacky way to enable instant UI updates.
Application.MainLoop.AddTimeout(TimeSpan.FromMilliseconds(600), a => true);
Application.MainLoop.AddTimeout(TimeSpan.FromSeconds(0.1), a => true);
Application.Run (new LoginView (new LoginViewModel ()));
}
}

35
ReactiveExample/README.md Normal file
View File

@@ -0,0 +1,35 @@
This is a sample app that shows how to use `System.Reactive` and `ReactiveUI` with `Terminal.Gui`. The app uses the MVVM architecture that may seem familiar to folks coming from WPF, Xamarin Forms, UWP, Avalonia, or Windows Forms. In this app, we implement the data bindings using ReactiveUI `WhenAnyValue` syntax and [Pharmacist](https://github.com/reactiveui/pharmacist) — a tool that converts all events in a NuGet package into observable wrappers.
<img src="https://user-images.githubusercontent.com/6759207/94748621-646a7280-038a-11eb-8ea0-34629dc799b3.gif" width="450">
### Data Bindings
If you wish to implement `OneWay` data binding, then use the `WhenAnyValue` [ReactiveUI extension method](https://www.reactiveui.net/docs/handbook/when-any/) that listens to `INotifyPropertyChanged` events of the specified property, and converts that events into `IObservable<TProperty>`:
```cs
// 'usernameInput' is 'TextField'
ViewModel
.WhenAnyValue (x => x.Username)
.BindTo (usernameInput, x => x.Text);
```
Note that your view model should implement `INotifyPropertyChanged` or inherit from a `ReactiveObject`. If you wish to implement `OneWayToSource` data binding, then install [Pharmacist.MSBuild](https://github.com/reactiveui/pharmacist) into your project and listen to e.g. `TextChanged` event of a `TextField`:
```cs
// 'usernameInput' is 'TextField'
usernameInput
.Events () // The Events() extension is generated by Pharmacist.
.TextChanged
.Select (old => usernameInput.Text)
.DistinctUntilChanged ()
.BindTo (ViewModel, x => x.Username);
```
If you combine `OneWay` and `OneWayToSource` data bindings, you get `TwoWay` data binding. Also be sure to use the `ustring` type instead of the `string` type. Invoking commands should be as simple as this:
```cs
// 'clearButton' is 'Button'
clearButton
.Events ()
.Clicked
.InvokeCommand (ViewModel, x => x.Clear);
```