22 people like it.
Like the snippet!
Building a WPF application in functional way
I started to write pure F# + WPF application in about half a year ago.
Today, I found a good way to compose WPF controls with dependent values.
It's only writing a dependency object type as a class and give it to constructors of GUI controls.
In this snippet "Volume","ColorVolume" and "ShapeContainer" has no properties.
But works as a View which represents internal Model and allows users to change internal data.
You only need calling a constructor of them.
It means that you can compose GUI controls and it's functionality as a immutable data structure.
(Update 2011/12/02 8:33:00(UTC+09:00) : Removed some user defined operators and renamed a type similar to DependencyObject in this snippet Reactor to SharedValue.)
(Update 2011/12/02 9:04:01(UTC+09:00) : renamed some variables..)
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:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
|
#r @"PresentationCore"
#r @"PresentationFramework"
#r @"WindowsBase"
#r @"System.Xaml"
#r @"UIAutomationTypes"
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Media
open System.Windows.Shapes
/// This operator is similar to (|>).
/// But, it returns argument as a return value.
/// Then you can chain functions which returns unit.
let ($) x f = f x ; x
/// This operator removes parenthesis..
let (=>) (o:IObservable<_>) f = o.Add f
let (<+>) = Observable.merge
/// Similar to applicative functor in Haskell. But the arguments are flipped
let (|+>) ev f = Observable.map f ev
type StackPanel with
/// Helper function to compose a GUI
member o.add x = o.Children.Add x |> ignore
/// This container is used by some controls to share a variable.
/// If the value is changed, it fires changed event.
/// Controls should have this instead of their own internal data
type Reactor<'a when 'a : equality>(value:'a) =
let mutable _value = value
let changed = Event<'a>()
member o.Get = _value
member o.Set value =
let old = _value
_value <- value
if old <> _value then _value |> changed.Trigger
member o.Changed = changed.Publish
/// Volume control , it shows a value and allows you to change it.
type Volume(title:string, range:int * int, re:Reactor<int>) as this =
inherit StackPanel(Orientation=Orientation.Horizontal)
do Label(Content=title,Width=50.) |>this.add
let label = Label(Content=re.Get,Width=50.) $ this.add
let slider = Slider(Minimum=float(fst range), Maximum=float(snd range), TickFrequency=2., Width=127.) $ this.add
let changedHandler value =
label.Content <- string value
slider.Value <- float value
do
slider.ValueChanged => fun arg -> int arg.NewValue |> re.Set
re.Get |> changedHandler
re.Changed => changedHandler
/// Volume control of a color
type ColorVolume (re:Reactor<Color>) as this =
inherit StackPanel(Orientation=Orientation.Vertical)
let alpha = Reactor(int re.Get.A)
let red = Reactor(int re.Get.R)
let green = Reactor(int re.Get.G)
let blue = Reactor(int re.Get.B)
do
alpha.Changed <+> red.Changed <+> green.Changed <+> blue.Changed => fun _ ->
re.Set(Color.FromArgb(byte alpha.Get,byte red.Get,byte green.Get,byte blue.Get))
re.Changed => fun color ->
alpha.Set (int color.A)
red.Set (int color.R)
green.Set (int color.G)
blue.Set (int color.B)
Volume("Alpha", (0,255), alpha) |> this.add
Volume("Red" , (0,255), red ) |> this.add
Volume("Green", (0,255), green) |> this.add
Volume("Blue" , (0,255), blue ) |> this.add
[<RequireQualifiedAccess>]
type MyShapes = Rectangle | Ellipse
/// Shape container control which reacts when properties of a shape is changed.
type ShapeContainer(shapes:Reactor<MyShapes>,width:Reactor<int>,height:Reactor<int>,color:Reactor<Color>) as this =
inherit Label(Width=250., Height=250.)
let mutable shape = Ellipse() :> Shape
let setWidth width = shape.Width <- float width
let setHeight height = shape.Height <- float height
let setColor color = shape.Fill <- SolidColorBrush(color)
let initShape () =
this.Content <- shape
setWidth width.Get
setHeight height.Get
setColor color.Get
let setShape du =
match du with
| MyShapes.Rectangle -> shape <- Rectangle()
| MyShapes.Ellipse -> shape <- Ellipse ()
initShape ()
do
initShape ()
width.Changed => setWidth
height.Changed => setHeight
color.Changed => setColor
shapes.Changed => setShape
/// This StackPanel contains every controls in this program
let stackPanel = StackPanel(Orientation=Orientation.Vertical)
/// Width reactor object
let width = Reactor(120)
Volume("Width",(50, 240),width) |> stackPanel.add // add a volume to the StackPanel
/// Height reactor object
let height = Reactor(80)
Volume("Height",(50, 200),height) |> stackPanel.add // add a volume to the StackPanel
/// Color reactor object
let color = Reactor(Colors.Blue)
ColorVolume(color) |> stackPanel.add // add volumes to the StackPanel
/// Shape reactor object
let shapes = Reactor(MyShapes.Ellipse)
let ellipseButton = Button(Content="Ellipse") $ stackPanel.add
let rectangleButton = Button(Content="Rectangle") $ stackPanel.add
ellipseButton.Click => fun _ -> shapes.Set MyShapes.Ellipse // add event handler to fire dependency calculation
rectangleButton.Click => fun _ -> shapes.Set MyShapes.Rectangle
// This is a shape control shown in the bottom of this program's window
ShapeContainer(shapes,width,height,color) |> stackPanel.add
// Make a window and show it
let window = Window(Title="F# is fun!",Width=260., Height=420., Content=stackPanel)
window.Show()
|
namespace System
namespace System.Windows
namespace System.Windows.Controls
namespace System.Windows.Media
namespace System.Windows.Shapes
val x : 'a
val f : ('a -> unit)
val o : IObservable<'a>
type IObservable<'T> =
member Subscribe : observer:IObserver<'T> -> IDisposable
Full name: System.IObservable<_>
member IObservable.Add : callback:('T -> unit) -> unit
module Observable
from Microsoft.FSharp.Control
val merge : source1:IObservable<'T> -> source2:IObservable<'T> -> IObservable<'T>
Full name: Microsoft.FSharp.Control.Observable.merge
val ev : IObservable<'a>
val f : ('a -> 'b)
val map : mapping:('T -> 'U) -> source:IObservable<'T> -> IObservable<'U>
Full name: Microsoft.FSharp.Control.Observable.map
Multiple items
type StackPanel =
inherit Panel
new : unit -> StackPanel
member CanHorizontallyScroll : bool with get, set
member CanVerticallyScroll : bool with get, set
member ExtentHeight : float
member ExtentWidth : float
member HorizontalOffset : float
member LineDown : unit -> unit
member LineLeft : unit -> unit
member LineRight : unit -> unit
member LineUp : unit -> unit
...
Full name: System.Windows.Controls.StackPanel
--------------------
StackPanel() : unit
val o : StackPanel
member StackPanel.add : x:UIElement -> unit
Full name: Script.add
Helper function to compose a GUI
val x : UIElement
property Panel.Children: UIElementCollection
UIElementCollection.Add(element: UIElement) : int
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
Multiple items
type Reactor<'a (requires equality)> =
new : value:'a -> Reactor<'a>
member Set : value:'a -> unit
member Changed : IEvent<'a>
member Get : 'a
Full name: Script.Reactor<_>
This container is used by some controls to share a variable.
If the value is changed, it fires changed event.
Controls should have this instead of their own internal data
--------------------
new : value:'a -> Reactor<'a>
val value : 'a (requires equality)
val changed : Event<'a> (requires equality)
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>
val o : Reactor<'a> (requires equality)
member Reactor.Get : 'a
Full name: Script.Reactor`1.Get
val mutable _value : 'a (requires equality)
Multiple items
member Reactor.Set : value:'a -> unit
Full name: Script.Reactor`1.Set
--------------------
module Set
from Microsoft.FSharp.Collections
--------------------
type Set<'T (requires comparison)> =
interface IComparable
interface IEnumerable
interface IEnumerable<'T>
interface ICollection<'T>
new : elements:seq<'T> -> Set<'T>
member Add : value:'T -> Set<'T>
member Contains : value:'T -> bool
override Equals : obj -> bool
member IsProperSubsetOf : otherSet:Set<'T> -> bool
member IsProperSupersetOf : otherSet:Set<'T> -> bool
...
Full name: Microsoft.FSharp.Collections.Set<_>
--------------------
new : elements:seq<'T> -> Set<'T>
val old : 'a (requires equality)
member Event.Trigger : arg:'T -> unit
member Reactor.Changed : IEvent<'a>
Full name: Script.Reactor`1.Changed
property Event.Publish: IEvent<'a>
Multiple items
type Volume =
inherit StackPanel
new : title:string * range:(int * int) * re:Reactor<int> -> Volume
Full name: Script.Volume
Volume control , it shows a value and allows you to change it.
--------------------
new : title:string * range:(int * int) * re:Reactor<int> -> Volume
val title : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
val range : int * int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val re : Reactor<int>
val this : Volume
type Orientation =
| Horizontal = 0
| Vertical = 1
Full name: System.Windows.Controls.Orientation
field Orientation.Horizontal = 0
Multiple items
type Label =
inherit ContentControl
new : unit -> Label
member Target : UIElement with get, set
static val TargetProperty : DependencyProperty
Full name: System.Windows.Controls.Label
--------------------
Label() : unit
val label : Label
property Reactor.Get: int
member StackPanel.add : x:UIElement -> unit
Helper function to compose a GUI
val slider : Slider
Multiple items
type Slider =
inherit RangeBase
new : unit -> Slider
member AutoToolTipPlacement : AutoToolTipPlacement with get, set
member AutoToolTipPrecision : int with get, set
member Delay : int with get, set
member Interval : int with get, set
member IsDirectionReversed : bool with get, set
member IsMoveToPointEnabled : bool with get, set
member IsSelectionRangeEnabled : bool with get, set
member IsSnapToTickEnabled : bool with get, set
member OnApplyTemplate : unit -> unit
...
Full name: System.Windows.Controls.Slider
--------------------
Slider() : unit
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
val fst : tuple:('T1 * 'T2) -> 'T1
Full name: Microsoft.FSharp.Core.Operators.fst
val snd : tuple:('T1 * 'T2) -> 'T2
Full name: Microsoft.FSharp.Core.Operators.snd
val changedHandler : (int -> unit)
val value : int
property ContentControl.Content: obj
property Primitives.RangeBase.Value: float
event Primitives.RangeBase.ValueChanged: IEvent<RoutedPropertyChangedEventHandler<float>,RoutedPropertyChangedEventArgs<float>>
val arg : RoutedPropertyChangedEventArgs<float>
property RoutedPropertyChangedEventArgs.NewValue: float
member Reactor.Set : value:'a -> unit
property Reactor.Changed: IEvent<int>
Multiple items
type ColorVolume =
inherit StackPanel
new : re:Reactor<Color> -> ColorVolume
Full name: Script.ColorVolume
Volume control of a color
--------------------
new : re:Reactor<Color> -> ColorVolume
val re : Reactor<Color>
type Color =
struct
member A : byte with get, set
member B : byte with get, set
member Clamp : unit -> unit
member ColorContext : ColorContext
member Equals : color:Color -> bool + 1 overload
member G : byte with get, set
member GetHashCode : unit -> int
member GetNativeColorValues : unit -> float32[]
member R : byte with get, set
member ScA : float32 with get, set
...
end
Full name: System.Windows.Media.Color
val this : ColorVolume
field Orientation.Vertical = 1
val alpha : Reactor<int>
property Reactor.Get: Color
property Color.A: byte
val red : Reactor<int>
property Color.R: byte
val green : Reactor<int>
property Color.G: byte
val blue : Reactor<int>
property Color.B: byte
Color.FromArgb(a: byte, r: byte, g: byte, b: byte) : Color
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.byte
--------------------
type byte = Byte
Full name: Microsoft.FSharp.Core.byte
property Reactor.Changed: IEvent<Color>
val color : Color
Multiple items
type RequireQualifiedAccessAttribute =
inherit Attribute
new : unit -> RequireQualifiedAccessAttribute
Full name: Microsoft.FSharp.Core.RequireQualifiedAccessAttribute
--------------------
new : unit -> RequireQualifiedAccessAttribute
type MyShapes =
| Rectangle
| Ellipse
Full name: Script.MyShapes
Multiple items
union case MyShapes.Rectangle: MyShapes
--------------------
type Rectangle =
inherit Shape
new : unit -> Rectangle
member GeometryTransform : Transform
member RadiusX : float with get, set
member RadiusY : float with get, set
member RenderedGeometry : Geometry
static val RadiusXProperty : DependencyProperty
static val RadiusYProperty : DependencyProperty
Full name: System.Windows.Shapes.Rectangle
--------------------
Rectangle() : unit
Multiple items
union case MyShapes.Ellipse: MyShapes
--------------------
type Ellipse =
inherit Shape
new : unit -> Ellipse
member GeometryTransform : Transform
member RenderedGeometry : Geometry
Full name: System.Windows.Shapes.Ellipse
--------------------
Ellipse() : unit
Multiple items
type ShapeContainer =
inherit Label
new : shapes:Reactor<MyShapes> * width:Reactor<int> * height:Reactor<int> * color:Reactor<Color> -> ShapeContainer
Full name: Script.ShapeContainer
Shape container control which reacts when properties of a shape is changed.
--------------------
new : shapes:Reactor<MyShapes> * width:Reactor<int> * height:Reactor<int> * color:Reactor<Color> -> ShapeContainer
val shapes : Reactor<MyShapes>
val width : Reactor<int>
val height : Reactor<int>
val color : Reactor<Color>
val this : ShapeContainer
val mutable shape : Shape
Multiple items
type Ellipse =
inherit Shape
new : unit -> Ellipse
member GeometryTransform : Transform
member RenderedGeometry : Geometry
Full name: System.Windows.Shapes.Ellipse
--------------------
Ellipse() : unit
type Shape =
inherit FrameworkElement
member Fill : Brush with get, set
member GeometryTransform : Transform
member RenderedGeometry : Geometry
member Stretch : Stretch with get, set
member Stroke : Brush with get, set
member StrokeDashArray : DoubleCollection with get, set
member StrokeDashCap : PenLineCap with get, set
member StrokeDashOffset : float with get, set
member StrokeEndLineCap : PenLineCap with get, set
member StrokeLineJoin : PenLineJoin with get, set
...
Full name: System.Windows.Shapes.Shape
val setWidth : (int -> unit)
val width : int
property FrameworkElement.Width: float
val setHeight : (int -> unit)
val height : int
property FrameworkElement.Height: float
val setColor : (Color -> unit)
property Shape.Fill: Brush
Multiple items
type SolidColorBrush =
inherit Brush
new : unit -> SolidColorBrush + 1 overload
member Clone : unit -> SolidColorBrush
member CloneCurrentValue : unit -> SolidColorBrush
member Color : Color with get, set
static val ColorProperty : DependencyProperty
static member DeserializeFrom : reader:BinaryReader -> obj
Full name: System.Windows.Media.SolidColorBrush
--------------------
SolidColorBrush() : unit
SolidColorBrush(color: Color) : unit
val initShape : (unit -> unit)
val setShape : (MyShapes -> unit)
val du : MyShapes
union case MyShapes.Rectangle: MyShapes
Multiple items
type Rectangle =
inherit Shape
new : unit -> Rectangle
member GeometryTransform : Transform
member RadiusX : float with get, set
member RadiusY : float with get, set
member RenderedGeometry : Geometry
static val RadiusXProperty : DependencyProperty
static val RadiusYProperty : DependencyProperty
Full name: System.Windows.Shapes.Rectangle
--------------------
Rectangle() : unit
union case MyShapes.Ellipse: MyShapes
property Reactor.Changed: IEvent<MyShapes>
val stackPanel : StackPanel
Full name: Script.stackPanel
This StackPanel contains every controls in this program
val width : Reactor<int>
Full name: Script.width
Width reactor object
val height : Reactor<int>
Full name: Script.height
Height reactor object
val color : Reactor<Color>
Full name: Script.color
Color reactor object
type Colors =
static member AliceBlue : Color
static member AntiqueWhite : Color
static member Aqua : Color
static member Aquamarine : Color
static member Azure : Color
static member Beige : Color
static member Bisque : Color
static member Black : Color
static member BlanchedAlmond : Color
static member Blue : Color
...
Full name: System.Windows.Media.Colors
property Colors.Blue: Color
val shapes : Reactor<MyShapes>
Full name: Script.shapes
Shape reactor object
val ellipseButton : Button
Full name: Script.ellipseButton
Multiple items
type Button =
inherit ButtonBase
new : unit -> Button
member IsCancel : bool with get, set
member IsDefault : bool with get, set
member IsDefaulted : bool
static val IsDefaultProperty : DependencyProperty
static val IsCancelProperty : DependencyProperty
static val IsDefaultedProperty : DependencyProperty
Full name: System.Windows.Controls.Button
--------------------
Button() : unit
val rectangleButton : Button
Full name: Script.rectangleButton
event Primitives.ButtonBase.Click: IEvent<RoutedEventHandler,RoutedEventArgs>
val window : Window
Full name: Script.window
Multiple items
type Window =
inherit ContentControl
new : unit -> Window
member Activate : unit -> bool
member AllowsTransparency : bool with get, set
member Close : unit -> unit
member DialogResult : Nullable<bool> with get, set
member DragMove : unit -> unit
member Hide : unit -> unit
member Icon : ImageSource with get, set
member IsActive : bool
member Left : float with get, set
...
Full name: System.Windows.Window
--------------------
Window() : unit
Window.Show() : unit
More information