FSharp.Control.Reactive


Introduction to Observables

Prerequisits

This page contains some basic examples of how to use Observable's. This is by no means a complete stable example and should only be used for demonstration purposes.

Subscribing on Observable

The following example show how the Enabling/Disabling of a button can be controled by looking at the input of another input field (TextBox).

Enable Disable Button

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
open System
open FSharp.Control.Reactive
open System.Windows.Forms


let buttonForm = new Form ()
let btn = new Button()
let txb = new TextBox()

txb.TextChanged
|> Observable.startWith [EventArgs.Empty]
|> Observable.subscribe (fun _ -> btn.Enabled <- not (String.IsNullOrEmpty txb.Text))

btn.Text <- "OK"
btn.Top <- 20

buttonForm.Controls.Add txb
buttonForm.Controls.Add btn
buttonForm.Show ()

Transforming Observable

Incoming emits can be transformed using the mapping functionality. Following example show how the input of a textbox can be transformed into the reversed value.

Reversed

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
module String = 
    let rev (s : string) = new String (s.ToCharArray () |> Array.rev)

let input = new TextBox()
let reversed = new Label()

input.TextChanged
|> Observable.map (fun _ -> input.Text |> String.rev)
|> Observable.subscribe (fun x -> reversed.Text <- x)

reversed.Top <- 20
let reverseForm = new Form ()
reverseForm.Controls.Add input
reverseForm.Controls.Add reversed
reverseForm.Show ()

Merging Two emits into one

Merging multiple events into one can be done with the Observable.merge primitive. Following example shows how we change the value of a label with the buttons ("Red" and "Green").

Red-Green

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
let redBtn = new Button()
let greenBtn = new Button()
let result = new Label()

let red = redBtn.Click |> Observable.mapTo "Red"
let green = greenBtn.Click |> Observable.mapTo "Green"

Observable.merge red green
|> Observable.subscribe (fun x -> result.Text <- x)

redBtn.Text <- "Red"
greenBtn.Text <- "Green"
greenBtn.Top <- 20
result.Left <- 100
let colorForm = new Form ()
colorForm.Controls.Add redBtn
colorForm.Controls.Add greenBtn
colorForm.Controls.Add result
colorForm.Show ()
namespace System
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Control

--------------------
namespace Microsoft.FSharp.Control
namespace FSharp.Control.Reactive
namespace System.Windows
val buttonForm : obj
val btn : obj
val txb : obj
Multiple items
module Observable

from FSharp.Control.Reactive

--------------------
module Observable

from Microsoft.FSharp.Control
val startWith : values:#seq<'T> -> source:IObservable<'T> -> IObservable<'T>
Multiple items
type EventArgs =
  new : unit -> EventArgs
  static val Empty : EventArgs

--------------------
EventArgs() : EventArgs
field EventArgs.Empty: EventArgs
Multiple items
val subscribe : onNext:('T -> unit) -> observable:IObservable<'T> -> IDisposable

--------------------
val subscribe : callback:('T -> unit) -> source:IObservable<'T> -> IDisposable
val not : value:bool -> bool
Multiple items
type String =
  new : value:char[] -> string + 8 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool + 3 overloads
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 3 overloads
  member EnumerateRunes : unit -> StringRuneEnumerator
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  ...

--------------------
String(value: char []) : String
String(value: nativeptr<char>) : String
String(value: nativeptr<sbyte>) : String
String(value: ReadOnlySpan<char>) : String
String(c: char, count: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
String.IsNullOrEmpty(value: string) : bool
val rev : s:string -> String
val s : string
Multiple items
val string : value:'T -> string

--------------------
type string = String
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...
val rev : array:'T [] -> 'T []
val input : obj
val reversed : obj
Multiple items
val map : f:('a -> 'b) -> source:IObservable<'a> -> IObservable<'b>

--------------------
val map : mapping:('T -> 'U) -> source:IObservable<'T> -> IObservable<'U>
val x : String
val reverseForm : obj
val redBtn : obj
val greenBtn : obj
val result : obj
val red : IObservable<string>
val mapTo : x:'a -> source:IObservable<'T> -> IObservable<'a>
val green : IObservable<string>
Multiple items
val merge : second:IObservable<'T> -> first:IObservable<'T> -> IObservable<'T>

--------------------
val merge : source1:IObservable<'T> -> source2:IObservable<'T> -> IObservable<'T>
val x : string
val colorForm : obj
Fork me on GitHub