Files
Terminal.Gui/ReactiveExample
Tig 6851b42a49 Fixes #2921 - MainLoop refactoring (#2922)
* Adds basic MainLoop unit tests

* Remove WinChange action from Curses

* Remove WinChange action from Curses

* Remove ProcessInput action from Windows MainLoop

* Simplified MainLoop/ConsoleDriver by making MainLoop internal and moving impt fns to Application

* Modernized Terminal resize events

* Modernized Terminal resize events

* Removed un used property

* for _isWindowsTerminal devenv->wininit; not sure what changed

* Modernized mouse/keyboard events (Action->EventHandler)

* Updated OnMouseEvent API docs

* Using WT_SESSION to detect WT

* removes hacky GetParentProcess

* Updates to fix #2634 (clear last line)

* removes hacky GetParentProcess2

* Addressed mac resize issue

* Addressed mac resize issue

* Removes ConsoleDriver.PrepareToRun, has Init return MainLoop

* Removes unneeded Attribute methods

* Removed GetProcesssName

* Removed GetProcesssName

* Refactored KeyEvent and KeyEventEventArgs into a single class

* Revert "Refactored KeyEvent and KeyEventEventArgs into a single class"

This reverts commit 88a00658db.

* Fixed key repeat issue; reverted stupidity on 1049/1047 confusion

* Updated CSI API Docs

* merge
2023-10-21 08:06:04 -07:00
..
2023-10-13 23:58:49 -06:00

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 — a tool that converts all events in a NuGet package into observable wrappers.

Scheduling

In order to use reactive extensions scheduling, copy-paste the TerminalScheduler.cs file into your project, and add the following lines to the composition root of your Terminal.Gui application:

Application.Init ();
RxApp.MainThreadScheduler = TerminalScheduler.Default;
RxApp.TaskpoolScheduler = TaskPoolScheduler.Default;
Application.Run (new RootView (new RootViewModel ()));

From now on, you can use .ObserveOn(RxApp.MainThreadScheduler) to return to the main loop from a background thread. This is useful when you have a IObservable<TValue> updated from a background thread, and you wish to update the UI with TValues received from that observable.

Data Bindings

If you wish to implement OneWay data binding, then use the WhenAnyValue ReactiveUI extension method that listens to INotifyPropertyChanged events of the specified property, and converts that events into IObservable<TProperty>:

// '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 into your project and listen to e.g. TextChanged event of a TextField:

// '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 string type instead of the string type. Invoking commands should be as simple as this:

// 'clearButton' is 'Button'
clearButton
	.Events ()
	.Clicked
	.InvokeCommand (ViewModel, x => x.Clear);