Saturday, January 19, 2008

Learning WPF with F# - ListBox Selection

I think I'm getting a better handle of F# programming language. For the sample codes in Chapter 13, I've decided to assume a Perl hacker mentality, which is try to do the same thing in different ways. One example is shown in the SelectColorGrid example. At the start of this series of blogs on F# and WPF, I would have written the following chunk of F# code for adding FrameworkElementFactory based on Rectangle to the FrameworkElementFactory based on StackPanel:

let factoryStack = new FrameworkElementFactory(typeof<StackPanel>)

let factoryRectangle = new FrameworkElementFactory(typeof<Rectangle>)
factoryRectangle.SetValue(Rectangle.WidthProperty,16.0)
factoryRectangle.SetValue(Rectangle.HeightProperty,16.0)
factoryRectangle.SetValue(Rectangle.MarginProperty,new Thickness(2.0)
factoryRectangle.SetValue(Rectangle.StrokeProperty,SystemColors.WindowTextBrush)
factoryRectangle.SetBinding(Rectangle.FillProperty,new Binding("Brush"))

factoryStack.AppendChild(factoryRectangle)

With some experimentation, I would can rewrite the above chunk of code as:

let factoryStack = new FrameworkElementFactory(typeof<StackPanel>)

factoryStack.AppendChild
  (let factory = new FrameworkElementFactory(typeof<Rectangle>)
  
   let setval = factory.SetValue
   let setbind = factory.SetBinding
   
   (Rectangle.WidthProperty,16.0)                          |> setval
   (Rectangle.HeightProperty,16.0)                         |> setval
   (Rectangle.MarginProperty,new Thickness(2.0))           |> setval
   (Rectangle.StrokeProperty,SystemColors.WindowTextBrush) |> setval
   (Rectangle.FillProperty,new Binding("Brush"))           |> setbind
   factory)

A couple points on the revised version of the example code. Note that I was able to define what I call function aliases via let setval = factory.SetValue. These provide syntatic sugar so I don't have to write verbose code. I can do this only because I can limit the scope within parameter tuple to factoryStack.AppendChild. I don't have to worry about setval being used elsewhere in my code. The other usage is flipping the order of parameter and function name with the forward pipe operator (|>). I'm not sure if this makes the code any more readable, but it sure warps my way of thinking when it comes to writing code and allows me to think about the code differently. The other operator I hope to take more advantage of is the forward composition operator (>>).

Another interesting representation of the code is implemented in the ColorWheel example. I'm able to isolate an entire chunk of functionality without exposing any variable. This ensures that I don't have to worry about change impacts to any other parts of the code. Here is where I wish I had a better understanding of F# compiler works and be able to tell which representation allows for better compiler optimization. When I'm ready for the challenge, I'm going to try to read through Simon Peyton Jone's book The Implementation of Functional Programming Languages which he has made freely available online.

Here are more code examples from working on Chapter 13 of Petzold's book Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation.


ListColorNames

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 13 - ListColorNames
//
let lstbox = new ListBox(Width=150.0,Height=150.0)

// Fill ListBox with Color names
let props = (typeof<Colors>).GetProperties()
props |> Seq.iter (fun p -> lstbox.Items.Add(p.Name)|>ignore)

let window = new Window(Title="List Color Names",Content=lstbox)

// ListBoxOnSelectionChanged
lstbox.SelectionChanged.Add( fun _ ->
let str = lstbox.SelectedItem :?> string
if str <> null then
let clr = (typeof<Colors>).GetProperty(str).GetValue(null,null) :?> Color
window.Background <- new SolidColorBrush(clr))

#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ListColorValues

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 13 - ListColorValues
//
let lstbox = new ListBox(Width=150.0,Height=150.0)

// Fill ListBox with Color values
let props = (typeof<Colors>).GetProperties()
props |> Seq.iter (fun p -> lstbox.Items.Add(p.GetValue(null,null))|>ignore)

let window = new Window(Title="List Color Values",Content=lstbox)

// ListBoxOnSelectionChanged
lstbox.SelectionChanged.Add( fun _ ->
if lstbox.SelectedIndex <> -1 then
let clr = lstbox.SelectedItem :?> Color
window.Background <- new SolidColorBrush(clr))

#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

NamedColor & ListNamedColors

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 13 - NamedColor
//
type NamedColor =
{clr : Color; clrname : string}

// Using v instead of this...
override v.ToString() = v.clrname
// normally I would write the above line of code as
// override this.ToString() = this.clrname. There's nothing
// special with "this"

member v.Name
with get() =
let retval = v.clrname |> String.map_concat (fun c ->
if Char.IsUpper(c)
then " " + String.of_char(c)
else String.of_char(c))
retval.Trim()

member v.Color
with get() = v.clr

static member All =
let props = (typeof<Colors>).GetProperties()
props |> Seq.map (fun prop -> {clrname=prop.Name; clr=(prop.GetValue(null,null):?>Color)})
//
// From Chapter 13 - ListNamedColors
//
let lstbox = new ListBox(ItemsSource=NamedColor.All,
DisplayMemberPath="Name",
SelectedValuePath="Color",
Width=150.0,
Height=150.0)

let window = new Window(Title="List Named Colors",Content=lstbox)

// ListBoxOnSelectionChanged
lstbox.SelectionChanged.Add( fun _ ->
if lstbox.SelectedValue <> null then
let clr = lstbox.SelectedValue :?> Color
window.Background <- new SolidColorBrush(clr))

#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

NamedBrush & ListNamedBrushes

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 13 - NamedBrush
//
type NamedBrush =
{brush : Brush; name : string}

override v.ToString() = v.name

member v.Name
with get() =
let retval = v.name |> String.map_concat (fun c ->
if Char.IsUpper(c)
then " " + String.of_char(c)
else String.of_char(c))
retval.Trim()

member v.Brush
with get() = v.brush

static member All =
let props = (typeof<Brushes>).GetProperties()
props |> Seq.map (fun prop -> {name=prop.Name; brush=(prop.GetValue(null,null):?>Brush)})
//
// From Chapter 13 - ListNamedBrushes
//
let lstbox = new ListBox(ItemsSource=NamedBrush.All,
DisplayMemberPath="Name",
SelectedValuePath="Brush",
Width=150.0,
Height=150.0)

let window = new Window(Title="List Named Brushes",Content=lstbox)

// Bind the SelectedValue to window Background
lstbox.SetBinding(ListBox.SelectedValueProperty,"Background")
lstbox.DataContext <- window

#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ListColorShapes

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes

//
// From Chapter 13 - ListColorShapes
//
let lstbox = new ListBox(Width=150.0,
Height=150.0)

// Fill ListBox with Ellipse objects
(typeof<Brushes>).GetProperties()
|> Seq.iter (fun prop ->
let brush = prop.GetValue(null,null) :?> Brush
let ellip = new Ellipse(Width=100.0,
Height = 25.0,
Margin = new Thickness(10.0,5.0,0.0,5.0),
Fill = brush)
lstbox.Items.Add(ellip)|>ignore)

let window = new Window(Title="List Color Shapes",Content=lstbox)

lstbox.SelectionChanged.Add( fun _ ->
if lstbox.SelectedIndex <> -1 then
window.Background <- (lstbox.SelectedItem :?> Shape).Fill)


#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ListColorLabels

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes

//
// From Chapter 13 - ListColorLabels
//
let lstbox = new ListBox(Width=150.0,
Height=150.0)

// Fill ListBox with label controls
(typeof<Colors>).GetProperties()
|> Seq.iter (fun prop ->
let clr = prop.GetValue(null,null) :?> Color
let b2f x = Float.of_int (Byte.to_int x)
let isBlack = 0.222*(b2f clr.R) + 0.707 * (b2f clr.G) + 0.071 * (b2f clr.B) > 128.0
let lbl = new Label(Content=prop.Name,
Background = new SolidColorBrush(clr),
Foreground = (if isBlack then Brushes.Black else Brushes.White),
Width = 100.0,
Margin = new Thickness(15.0,0.0,0.0,0.0),
Tag = clr)
lstbox.Items.Add(lbl) |> ignore)


let window = new Window(Title="List Color Labels",Content=lstbox)

lstbox.SelectionChanged.Add( fun _ ->
match lstbox.SelectedItem with
| :? Label as lbl ->
let clr = lbl.Tag :?> Color
window.Background <- new SolidColorBrush(clr)
| _ -> ())



#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ListWithListBoxItems

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media

//
// From Chapter 13 - ListWithListBoxItems
//
let lstbox = new ListBox(Width=150.0,
Height=150.0)

// Fill ListBox with label controls
(typeof<Colors>).GetProperties()
|> Seq.iter (fun prop ->
let clr = prop.GetValue(null,null) :?> Color

let isBlack =
let b2f x = Float.of_int (Byte.to_int x)
0.222*(b2f clr.R) + 0.707 * (b2f clr.G) + 0.071 * (b2f clr.B) > 128.0

let item = new ListBoxItem(Content=prop.Name,
Background = new SolidColorBrush(clr),
Foreground = (if isBlack then Brushes.Black else Brushes.White),
HorizontalContentAlignment = HorizontalAlignment.Center,
Padding = new Thickness(2.0))
lstbox.Items.Add(item) |> ignore)


let window = new Window(Title="List with ListBoxItem",Content=lstbox)

lstbox.SelectionChanged.Add( fun args ->
if args.RemovedItems.Count > 0 then
let item = args.RemovedItems.[0] :?> ListBoxItem
let str = item.Content :?> string
item.Content <- String.sub str 2 (str.Length-4)
item.FontWeight <- FontWeights.Regular

if args.AddedItems.Count > 0 then
let item = args.AddedItems.[0] :?> ListBoxItem
let str = item.Content :?> string
item.Content <- "[ " + str + " ]"
item.FontWeight <- FontWeights.Bold


match lstbox.SelectedItem with
| :? ListBoxItem as item ->
window.Background <- item.Background
| _ -> ())



#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ColorListBoxItem, ColorListBox & ListColorsElegantly

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes
//
// From Chapter 13 - ColorListBoxItem, ColorListBox & ListColorsElegantly
//
//
// From Chapter 13 - ColorListBoxItem
//
type ColorListBoxItem() = class
inherit ListBoxItem() as base

let mutable str = ""
let stack = new StackPanel(Orientation=Orientation.Horizontal)
let text = new TextBlock(VerticalAlignment = VerticalAlignment.Center)

let rect = new Rectangle(Width=16.0,
Height=16.0,
Margin=new Thickness(2.0),
Stroke = SystemColors.WindowTextBrush)
do
base.Content <- stack
stack.Children.Add(rect) |> ignore
stack.Children.Add(text) |> ignore

member this.Text
with get() = str
and set value =
str <- value
let strSpaced = value |> String.map_concat (fun c ->
if Char.IsUpper(c)
then " " + String.of_char(c)
else String.of_char(c))
text.Text <- strSpaced

member this.Color
with get() =
let brush = rect.Fill :?> SolidColorBrush
if brush = null then Colors.Transparent else brush.Color
and set value =
rect.Fill <- new SolidColorBrush(value)

override this.OnSelected (args:RoutedEventArgs) =
base.OnSelected(args)
text.FontWeight <- FontWeights.Bold

override this.OnUnselected (args:RoutedEventArgs) =
base.OnUnselected(args)
text.FontWeight <- FontWeights.Regular

override this.ToString() = str
end
//
// From Chapter 13 - ColorListBox
//
type ColorListBox() = class
inherit ListBox() as base

do
let this = base
typeof<Colors>.GetProperties()
|> Seq.iter (fun prop ->
let item = new ColorListBoxItem(Text=prop.Name,
Color=(prop.GetValue(null,null):?> Color))
this.Items.Add(item) |> ignore
())
this.SelectedValuePath <- "Color"

member this.SelectedColor
with get() = this.SelectedValue :?> Color
and set (value:Color) = this.SelectedValue <- value

end
//
// From Chapter 13 - ListColorsElegantly
//

let lstbox = new ColorListBox(SelectedColor = SystemColors.WindowColor,
Width=150.0,
Height=150.0)


let window = new Window(Title="List Colors Elegantly",Content=lstbox)

lstbox.SelectionChanged.Add( fun _ ->
window.Background <- new SolidColorBrush(lstbox.SelectedColor))


#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

NamedBrush & ListColorsEvenMoreElegantlier

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Reflection
open System.Windows
open System.Windows.Data
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes
//
// From Chapter 13 - NamedBrush
//
type NamedBrush =
{brush : Brush; name : string}

override v.ToString() = v.name

member v.Name
with get() =
let retval = v.name |> String.map_concat (fun c ->
if Char.IsUpper(c)
then " " + String.of_char(c)
else String.of_char(c))
retval.Trim()

member v.Brush
with get() = v.brush

static member All =
(typeof<Brushes>).GetProperties()
|> Seq.map (fun prop -> {name=prop.Name; brush=(prop.GetValue(null,null):?>Brush)})

//
// From Chapter 13 - ListColorsEvenMoreElegantlier
//

// Create a data template for the items and populate it.
let template =
let brushTemplate = new DataTemplate(typeof<NamedBrush>)

// Create a FrameworkElementFactory based on StackPanel
let factoryStack = new FrameworkElementFactory(typeof<StackPanel>)
factoryStack.SetValue(StackPanel.OrientationProperty,Orientation.Horizontal)

brushTemplate.VisualTree <- factoryStack

// Create a FrameworkElementFactory based on Rectangle and
// add it to the stack panel. Note, with this construct, I don't
// pollute the rest of the code with the factory definition
factoryStack.AppendChild
(let factory = new FrameworkElementFactory(typeof<Rectangle>)
// function aliases (syntactic sugar) so I don't have to type as much
let setval = factory.SetValue
let setbind = factory.SetBinding

// I flipped the parameters with function name via the forward (|>) operator
// Not sure is this is a more readable format or not, but it sure is a
// departure from normal C# forms.
(Rectangle.WidthProperty,16.0) |> setval
(Rectangle.HeightProperty,16.0) |> setval
(Rectangle.MarginProperty,new Thickness(2.0)) |> setval
(Rectangle.StrokeProperty,SystemColors.WindowTextBrush) |> setval
(Rectangle.FillProperty,new Binding("Brush")) |> setbind
factory)

// Create a FrameworkElementFactory based on TextBlock and add it to stack panel
(let factory = new FrameworkElementFactory(typeof<TextBlock>)
(TextBlock.VerticalAlignmentProperty,VerticalAlignment.Center) |> factory.SetValue
(TextBlock.TextProperty,new Binding("Name")) |> factory.SetBinding
factory) |> factoryStack.AppendChild

// return the data template
brushTemplate

// variables factoryRectangle, factoryStack & factoryTextBlock no longer pollute
// the rest of the code space

let lstbox = new ListBox(Width=150.0,
Height=150.0,
ItemTemplate=template,
ItemsSource=NamedBrush.All,
SelectedValuePath="Brush")


let window = new Window(Title="List Colors Even Elegantlier",Content=lstbox)
lstbox.SetBinding(ListBox.SelectedValueProperty,"Background")
lstbox.DataContext <- window

#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ColorGridBox & SelectColorFromGrid

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"

open System
open System.Windows
open System.Windows.Data
open System.Windows.Controls
open System.Windows.Controls.Primitives
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes
//
// From Chapter 13 - ColorGridBox
//
type ColorGridBox() as this = class
inherit ListBox() as base

// list of colors to be displayed
let strColors =
["Black"; "Brown"; "DarkGreen"; "MidnightBlue";
"Navy"; "DarkBlue"; "Indigo"; "DimGray";
"DarkRed"; "OrangeRed"; "Olive"; "Green";
"Teal"; "Blue"; "SlateGray"; "Gray";
"Red"; ""; "YellowGreen"; "SeaGreen";
"Aqua"; "LightBlue"; "Violet"; "DarkGray";
"Pink"; "Gold"; "Yellow"; "Lime";
"Turquoise"; "SkyBlue"; ""; "LightGray";
"LightPink"; "Tan"; "LightYellow"; "LightGreen";
"LightCyan"; "LightSkyBlue"; "Lavender"; "White"]

do
let factory = new FrameworkElementFactory(typeof<UniformGrid>)
(UniformGrid.ColumnsProperty,8) |> factory.SetValue

this.ItemsPanel <- new ItemsPanelTemplate(factory)

// Add items to the ListBox
strColors |> Seq.iter (fun color ->
(new Rectangle(Width=12.0,
Height=12.0,
Fill=(typeof<Brushes>.GetProperty(color).GetValue(null,null):?>Brush),
ToolTip = new ToolTip(Content=color),
Margin=new Thickness(4.0))
|> this.Items.Add |> ignore))

this.SelectedValuePath <- "Fill"
end
//
// From Chapter 13 - SelectColorFromGrid
//

let stack = new StackPanel()

let window = new Window(Title="Select Color from Grid",
SizeToContent = SizeToContent.WidthAndHeight,
Content=stack)

let createDoNothingButton () =
new Button(Content = "Do-nothing button\nto test tabbing",
Margin = new Thickness(24.0),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center)

// Create do-nothing button to test tabbing and add to StackPanel
createDoNothingButton () |> stack.Children.Add |> ignore

// Create ColorGridBox control and add to StackPanel
(let clrgrid = new ColorGridBox(Margin = new Thickness(24.0),
DataContext=window,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center)
// Bind background
(ColorGridBox.SelectedValueProperty,"Background")
|>clrgrid.SetBinding |> ignore
clrgrid) |> stack.Children.Add |> ignore

// Create another do-nothing button to test tabbing and add to StackPanel
createDoNothingButton () |> stack.Children.Add |> ignore


#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif

ColorWheel & SelectColorFromWheel

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"radialpanel.dll"

open System
open System.Windows
open System.Windows.Data
open System.Windows.Controls
open System.Windows.Controls.Primitives
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes
open Chapter12

//
// From Chapter 13 - ColorWheel
//
type ColorWheel() as this = class
inherit ListBox() as base

do
this.ItemsPanel <- new ItemsPanelTemplate
(new FrameworkElementFactory(typeof<RadialPanel>))

// Create DataTemplate for the items and create a FrameworkElementFactory
// based on Rectangle and use that factory for visual tree.
// Again, I'm just playing around with the expressiveness of F# and not
// necessarily recommending coding in this format.
// Look ma! No let bindings....
new DataTemplate(typeof<Brush>) |> (fun template ->
new FrameworkElementFactory(typeof<Rectangle>) |> (fun f ->
(Rectangle.WidthProperty, 4.0) |> f.SetValue
(Rectangle.HeightProperty, 12.0) |> f.SetValue
(Rectangle.MarginProperty, new Thickness(1.0, 8.0, 1.0, 8.0)) |> f.SetValue
(Rectangle.FillProperty, new Binding("")) |> f.SetBinding
template.VisualTree <- f)
this.ItemTemplate <- template)

// Set the items in the ListBox
(typeof<Brushes>).GetProperties() |> Seq.iter (fun prop ->
(prop.GetValue(null,null) :?> Brush) |> this.Items.Add |> ignore)
end
//
// From Chapter 13 - SelectColorFromWheel
//


let createDoNothingButton () =
new Button(Content = "Do-nothing button\nto test tabbing",
Margin = new Thickness(24.0),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center)

let stack = new StackPanel(Orientation=Orientation.Horizontal)

// Add a do-nothing button to test tabbing
createDoNothingButton() |> stack.Children.Add |> ignore

// Create ColorWheel Control
let clrwheel = new ColorWheel(Margin = new Thickness(24.0),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center)
clrwheel |> stack.Children.Add |> ignore

// Add another do-nothing button to test tabbing
createDoNothingButton() |> stack.Children.Add |> ignore


let window = new Window(Title="Select Color from Wheel",
SizeToContent = SizeToContent.WidthAndHeight,
Content=stack)

// Bind Background of window to selected value of ColorWheel
(ColorWheel.SelectedValueProperty, "Background") |> clrwheel.SetBinding
clrwheel.DataContext <- window

#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif


No comments: