3 people like it.
Like the snippet!
ViewModelBase with strongly typed PropertyChanged subscription
A base class for your view models with methods that take a code quotation of a property to subscribe to, and makes use of IObservable for strongly typed subscriptions to PropertyChanged for this property.
You might want to use System.Reactive and insert `Observable.DistinctUntilChanged` after `Observable.map`.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
|
open System
open System.ComponentModel
open Microsoft.FSharp.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
type ViewModelBase() =
/// Evaluates an expression. From http://www.fssnip.net/h1
let rec eval = function
| Value (v, _) -> v
| Coerce (e, _) -> eval e
| NewObject (ci, args) -> ci.Invoke (evalAll args)
| NewArray (t, args) ->
let array = Array.CreateInstance (t, args.Length)
args |> List.iteri (fun i arg -> array.SetValue (eval arg, i))
box array
| NewUnionCase (case, args) -> FSharpValue.MakeUnion (case, evalAll args)
| NewRecord (t, args) -> FSharpValue.MakeRecord (t, evalAll args)
| NewTuple args ->
let t = FSharpType.MakeTupleType [| for arg in args -> arg.Type |]
FSharpValue.MakeTuple (evalAll args, t)
| FieldGet (Some (Value (v, _)), fi) -> fi.GetValue v
| PropertyGet (None, pi, args) -> pi.GetValue (null, evalAll args)
| PropertyGet (Some x, pi, args) -> pi.GetValue (eval x, evalAll args)
| Call (None, mi, args) -> mi.Invoke (null, evalAll args)
| Call (Some x, mi, args) -> mi.Invoke (eval x, evalAll args)
| x -> raise <| NotSupportedException(string x)
and evalAll args = [| for arg in args -> eval arg |]
let propertyChanged = new Event<_,_>()
interface INotifyPropertyChanged with
[<CLIEvent>]
member __.PropertyChanged = propertyChanged.Publish
member this.OnPropertyChanged(propertyName : string) =
propertyChanged.Trigger(this, new PropertyChangedEventArgs(propertyName))
/// Returns an observable that publishes the value of the quoted property each
/// time INotifyPropertyChanged is raised for this property.
member this.Observe (getProperty: Expr<'a>) : IObservable<'a> =
match getProperty with
| PropertyGet (_, propInfo, _) ->
(this :> INotifyPropertyChanged).PropertyChanged
|> Observable.filter (fun args -> args.PropertyName = propInfo.Name)
|> Observable.map (fun _ -> eval getProperty :?> 'a)
| _ -> failwith "Expression must be a property getter"
/// Calls the callback with the value of the expression every time
/// INotifyPropertyChanged is raised for this property.
member this.Subscribe (getProperty: Expr<'a>) (callback: 'a -> unit) : unit =
this.Observe getProperty |> Observable.add callback
/// Calls the callback with the value of the expression every time
/// INotifyPropertyChanged is raised for this property. Also calls the callback
/// immediately with the current value of the expression.
member this.SubscribeAndInit (getProperty: Expr<'a>) (callback: 'a -> unit) : unit =
this.Subscribe getProperty callback
eval getProperty :?> 'a |> callback
/// Usage
type MyVm() =
inherit ViewModelBase()
member val MyInt = 0 with get, set
let vm = MyVm()
vm.SubscribeAndInit <@ vm.MyInt @> (fun i -> printfn "Value is %i" i)
|
namespace System
namespace System.ComponentModel
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Reflection
namespace Microsoft.FSharp.Quotations
module Patterns
from Microsoft.FSharp.Quotations
Multiple items
type ViewModelBase =
interface INotifyPropertyChanged
new : unit -> ViewModelBase
member Observe : getProperty:Expr<'a> -> IObservable<'a>
member OnPropertyChanged : propertyName:string -> unit
member Subscribe : getProperty:Expr<'a> -> callback:('a -> unit) -> unit
member SubscribeAndInit : getProperty:Expr<'a> -> callback:('a -> unit) -> unit
Full name: Script.ViewModelBase
--------------------
new : unit -> ViewModelBase
val eval : (Expr -> obj)
Evaluates an expression. From http://www.fssnip.net/h1
active recognizer Value: Expr -> (obj * Type) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |Value|_| )
val v : obj
active recognizer Coerce: Expr -> (Expr * Type) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |Coerce|_| )
val e : Expr
active recognizer NewObject: Expr -> (Reflection.ConstructorInfo * Expr list) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |NewObject|_| )
val ci : Reflection.ConstructorInfo
val args : Expr list
Reflection.ConstructorInfo.Invoke(parameters: obj []) : obj
Reflection.MethodBase.Invoke(obj: obj, parameters: obj []) : obj
Reflection.ConstructorInfo.Invoke(invokeAttr: Reflection.BindingFlags, binder: Reflection.Binder, parameters: obj [], culture: Globalization.CultureInfo) : obj
Reflection.MethodBase.Invoke(obj: obj, invokeAttr: Reflection.BindingFlags, binder: Reflection.Binder, parameters: obj [], culture: Globalization.CultureInfo) : obj
val evalAll : (Expr list -> obj [])
active recognizer NewArray: Expr -> (Type * Expr list) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |NewArray|_| )
val t : Type
Multiple items
val array : Array
--------------------
type 'T array = 'T []
Full name: Microsoft.FSharp.Core.array<_>
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
...
Full name: System.Array
Array.CreateInstance(elementType: Type, [<ParamArray>] lengths: int64 []) : Array
Array.CreateInstance(elementType: Type, [<ParamArray>] lengths: int []) : Array
Array.CreateInstance(elementType: Type, length: int) : Array
Array.CreateInstance(elementType: Type, lengths: int [], lowerBounds: int []) : Array
Array.CreateInstance(elementType: Type, length1: int, length2: int) : Array
Array.CreateInstance(elementType: Type, length1: int, length2: int, length3: int) : Array
property List.Length: int
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
val iteri : action:(int -> 'T -> unit) -> list:'T list -> unit
Full name: Microsoft.FSharp.Collections.List.iteri
val i : int
val arg : Expr
Array.SetValue(value: obj, [<ParamArray>] indices: int64 []) : unit
Array.SetValue(value: obj, index: int64) : unit
Array.SetValue(value: obj, [<ParamArray>] indices: int []) : unit
Array.SetValue(value: obj, index: int) : unit
Array.SetValue(value: obj, index1: int64, index2: int64) : unit
Array.SetValue(value: obj, index1: int, index2: int) : unit
Array.SetValue(value: obj, index1: int64, index2: int64, index3: int64) : unit
Array.SetValue(value: obj, index1: int, index2: int, index3: int) : unit
val box : value:'T -> obj
Full name: Microsoft.FSharp.Core.Operators.box
active recognizer NewUnionCase: Expr -> (UnionCaseInfo * Expr list) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |NewUnionCase|_| )
val case : UnionCaseInfo
type FSharpValue =
static member GetExceptionFields : exn:obj * ?bindingFlags:BindingFlags -> obj []
static member GetRecordField : record:obj * info:PropertyInfo -> obj
static member GetRecordFields : record:obj * ?bindingFlags:BindingFlags -> obj []
static member GetTupleField : tuple:obj * index:int -> obj
static member GetTupleFields : tuple:obj -> obj []
static member GetUnionFields : value:obj * unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo * obj []
static member MakeFunction : functionType:Type * implementation:(obj -> obj) -> obj
static member MakeRecord : recordType:Type * values:obj [] * ?bindingFlags:BindingFlags -> obj
static member MakeTuple : tupleElements:obj [] * tupleType:Type -> obj
static member MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?bindingFlags:BindingFlags -> obj
...
Full name: Microsoft.FSharp.Reflection.FSharpValue
static member FSharpValue.MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?allowAccessToPrivateRepresentation:bool -> obj
static member FSharpValue.MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?bindingFlags:Reflection.BindingFlags -> obj
active recognizer NewRecord: Expr -> (Type * Expr list) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |NewRecord|_| )
static member FSharpValue.MakeRecord : recordType:Type * values:obj [] * ?allowAccessToPrivateRepresentation:bool -> obj
static member FSharpValue.MakeRecord : recordType:Type * values:obj [] * ?bindingFlags:Reflection.BindingFlags -> obj
active recognizer NewTuple: Expr -> Expr list option
Full name: Microsoft.FSharp.Quotations.Patterns.( |NewTuple|_| )
type FSharpType =
static member GetExceptionFields : exceptionType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
static member GetFunctionElements : functionType:Type -> Type * Type
static member GetRecordFields : recordType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
static member GetTupleElements : tupleType:Type -> Type []
static member GetUnionCases : unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo []
static member IsExceptionRepresentation : exceptionType:Type * ?bindingFlags:BindingFlags -> bool
static member IsFunction : typ:Type -> bool
static member IsModule : typ:Type -> bool
static member IsRecord : typ:Type * ?bindingFlags:BindingFlags -> bool
static member IsTuple : typ:Type -> bool
...
Full name: Microsoft.FSharp.Reflection.FSharpType
static member FSharpType.MakeTupleType : types:Type [] -> Type
property Expr.Type: Type
static member FSharpValue.MakeTuple : tupleElements:obj [] * tupleType:Type -> obj
active recognizer FieldGet: Expr -> (Expr option * Reflection.FieldInfo) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |FieldGet|_| )
union case Option.Some: Value: 'T -> Option<'T>
val fi : Reflection.FieldInfo
Reflection.FieldInfo.GetValue(obj: obj) : obj
active recognizer PropertyGet: Expr -> (Expr option * Reflection.PropertyInfo * Expr list) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |PropertyGet|_| )
union case Option.None: Option<'T>
val pi : Reflection.PropertyInfo
Reflection.PropertyInfo.GetValue(obj: obj, index: obj []) : obj
Reflection.PropertyInfo.GetValue(obj: obj, invokeAttr: Reflection.BindingFlags, binder: Reflection.Binder, index: obj [], culture: Globalization.CultureInfo) : obj
val x : Expr
active recognizer Call: Expr -> (Expr option * Reflection.MethodInfo * Expr list) option
Full name: Microsoft.FSharp.Quotations.Patterns.( |Call|_| )
val mi : Reflection.MethodInfo
Reflection.MethodBase.Invoke(obj: obj, parameters: obj []) : obj
Reflection.MethodBase.Invoke(obj: obj, invokeAttr: Reflection.BindingFlags, binder: Reflection.Binder, parameters: obj [], culture: Globalization.CultureInfo) : obj
val raise : exn:Exception -> 'T
Full name: Microsoft.FSharp.Core.Operators.raise
Multiple items
type NotSupportedException =
inherit SystemException
new : unit -> NotSupportedException + 2 overloads
Full name: System.NotSupportedException
--------------------
NotSupportedException() : unit
NotSupportedException(message: string) : unit
NotSupportedException(message: string, innerException: exn) : unit
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
val propertyChanged : Event<PropertyChangedEventHandler,PropertyChangedEventArgs>
Multiple items
module Event
from Microsoft.FSharp.Control
--------------------
type Event<'T> =
new : unit -> Event<'T>
member Trigger : arg:'T -> unit
member Publish : IEvent<'T>
Full name: Microsoft.FSharp.Control.Event<_>
--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
new : unit -> Event<'Delegate,'Args>
member Trigger : sender:obj * args:'Args -> unit
member Publish : IEvent<'Delegate,'Args>
Full name: Microsoft.FSharp.Control.Event<_,_>
--------------------
new : unit -> Event<'T>
--------------------
new : unit -> Event<'Delegate,'Args>
type INotifyPropertyChanged =
event PropertyChanged : PropertyChangedEventHandler
Full name: System.ComponentModel.INotifyPropertyChanged
Multiple items
type CLIEventAttribute =
inherit Attribute
new : unit -> CLIEventAttribute
Full name: Microsoft.FSharp.Core.CLIEventAttribute
--------------------
new : unit -> CLIEventAttribute
val __ : ViewModelBase
override ViewModelBase.PropertyChanged : IEvent<PropertyChangedEventHandler,PropertyChangedEventArgs>
Full name: Script.ViewModelBase.PropertyChanged
property Event.Publish: IEvent<PropertyChangedEventHandler,PropertyChangedEventArgs>
val this : ViewModelBase
member ViewModelBase.OnPropertyChanged : propertyName:string -> unit
Full name: Script.ViewModelBase.OnPropertyChanged
val propertyName : string
member Event.Trigger : sender:obj * args:'Args -> unit
Multiple items
type PropertyChangedEventArgs =
inherit EventArgs
new : propertyName:string -> PropertyChangedEventArgs
member PropertyName : string
Full name: System.ComponentModel.PropertyChangedEventArgs
--------------------
PropertyChangedEventArgs(propertyName: string) : unit
member ViewModelBase.Observe : getProperty:Expr<'a> -> IObservable<'a>
Full name: Script.ViewModelBase.Observe
Returns an observable that publishes the value of the quoted property each
time INotifyPropertyChanged is raised for this property.
val getProperty : Expr<'a>
Multiple items
type Expr =
override Equals : obj:obj -> bool
member GetFreeVars : unit -> seq<Var>
member Substitute : substitution:(Var -> Expr option) -> Expr
member ToString : full:bool -> string
member CustomAttributes : Expr list
member Type : Type
static member AddressOf : target:Expr -> Expr
static member AddressSet : target:Expr * value:Expr -> Expr
static member Application : functionExpr:Expr * argument:Expr -> Expr
static member Applications : functionExpr:Expr * arguments:Expr list list -> Expr
...
Full name: Microsoft.FSharp.Quotations.Expr
--------------------
type Expr<'T> =
inherit Expr
member Raw : Expr
Full name: Microsoft.FSharp.Quotations.Expr<_>
type IObservable<'T> =
member Subscribe : observer:IObserver<'T> -> IDisposable
Full name: System.IObservable<_>
val propInfo : Reflection.PropertyInfo
module Observable
from Microsoft.FSharp.Control
val filter : predicate:('T -> bool) -> source:IObservable<'T> -> IObservable<'T>
Full name: Microsoft.FSharp.Control.Observable.filter
val args : PropertyChangedEventArgs
property PropertyChangedEventArgs.PropertyName: string
property Reflection.MemberInfo.Name: string
val map : mapping:('T -> 'U) -> source:IObservable<'T> -> IObservable<'U>
Full name: Microsoft.FSharp.Control.Observable.map
val failwith : message:string -> 'T
Full name: Microsoft.FSharp.Core.Operators.failwith
member ViewModelBase.Subscribe : getProperty:Expr<'a> -> callback:('a -> unit) -> unit
Full name: Script.ViewModelBase.Subscribe
Calls the callback with the value of the expression every time
INotifyPropertyChanged is raised for this property.
val callback : ('a -> unit)
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
member ViewModelBase.Observe : getProperty:Expr<'a> -> IObservable<'a>
Returns an observable that publishes the value of the quoted property each
time INotifyPropertyChanged is raised for this property.
val add : callback:('T -> unit) -> source:IObservable<'T> -> unit
Full name: Microsoft.FSharp.Control.Observable.add
member ViewModelBase.SubscribeAndInit : getProperty:Expr<'a> -> callback:('a -> unit) -> unit
Full name: Script.ViewModelBase.SubscribeAndInit
Calls the callback with the value of the expression every time
INotifyPropertyChanged is raised for this property. Also calls the callback
immediately with the current value of the expression.
member ViewModelBase.Subscribe : getProperty:Expr<'a> -> callback:('a -> unit) -> unit
Calls the callback with the value of the expression every time
INotifyPropertyChanged is raised for this property.
Multiple items
type MyVm =
inherit ViewModelBase
new : unit -> MyVm
member MyInt : int
member MyInt : int with set
Full name: Script.MyVm
Usage
--------------------
new : unit -> MyVm
val set : elements:seq<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val vm : MyVm
Full name: Script.vm
member ViewModelBase.SubscribeAndInit : getProperty:Expr<'a> -> callback:('a -> unit) -> unit
Calls the callback with the value of the expression every time
INotifyPropertyChanged is raised for this property. Also calls the callback
immediately with the current value of the expression.
property MyVm.MyInt: int
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
More information