Friday, November 23, 2007

Learning WPF with F# - The Dock and the Grid

Working through Chapter 6 of Petzold's "Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation" book.

There are some new concepts that I ran into while working through the examples on Chapter 6. The first is how to deal with C# out parameters in the code DateTime.TryParse. So I fired up the interactive F# system and did a type dump on DateTime.TryParse. The results from F# interactive is val it : string -> bool * DateTime. Now it becomes clear how F# deals with out parameters. C# is limited to a single return value while F# is not constrained by that. For any methods that has has more then one return values, they're packaged as tuples. So I can grab the return value from DateTime.TryParse as follows:

let (flag,date) = DateTime.TryParse("1/1/2007")

The second concept that I'm struggling with and trying to work through is how to write expressively elegant code that I see in Haskell's Prelude code such as zip and zipWidth. I guess I still have not full grok the combinator capability of functional programming to develop expressively elegant codes. I suspect this is just from the lack of experience in working with functional languages and hopefully, by working with functional languages more, I'll be able to develop more expressive and elegant code. I wish there was a book similar to Programming Pearls combined with Functional Programming Cookbook that would illustrate the expressive power of functional programming. In any case, I'm not able to find an equivalent zip and zipWith function in F#, so I decided to recreate Haskell's version in F#. My initial version was much more verbose then the Haskell version. But I went and took a look at the code samples from Don Syme's new book, "Expert F#" and found a way to rewrite it so that it's similarly compact compared with the Haskell version. Nothing like learning from the original creator of F#.

Here's the original version of zip and zipWidth from the Prelude library in Haskell:

zip              :: [a] -> [b] -> [(a,b)]
zip               = zipWith  (\a b -> (a,b))

zipWith                  :: (a->b->c) -> [a]->[b]->[c]
zipWith z (a:as) (b:bs)   = z a b : zipWith z as bs
zipWith _ _      _        = []


DockAroundTheBlock

#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.Controls
open System.Windows.Input
open System.Windows.Media

(* Could not cast to dock, create explicit mapping for Dock property *)
let getdock i =
match i with
| 0 -> Dock.Left
| 1 -> Dock.Top
| 2 -> Dock.Right
| 3 -> Dock.Bottom
| _ -> failwith "Not an option"

(* From Chap 5 - DockAroundTheBlock.cs *)
type DockAroundTheBlock = class
inherit Window as base

new () as this = {} then
this.Title <- "Dock Around the Block"
let dock = new DockPanel()
this.Content <- dock

let addButton i =
let btn = new Button()
btn.Content <- "Button No. " + Int32.to_string(i+1)
dock.Children.Add(btn) |>ignore
btn.SetValue(DockPanel.DockProperty, getdock (i%4)) |> ignore

List.iter addButton [0..16]
end

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

MeetTheDockers

#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.Controls
open System.Windows.Controls.Primitives
open System.Windows.Input
open System.Windows.Media

(* From Chap 5 - MeetTheDockers.cs *)
type MeetTheDockers = class
inherit Window as base

new () as this = {} then
this.Title <- "Meet the Dockers"
let dock = new DockPanel()
this.Content <- dock

let menu = new Menu()
let item = new MenuItem()
item.Header <- "Menu"
menu.Items.Add(item) |> ignore

// Dock menu at top of panel
DockPanel.SetDock(menu,Dock.Top) |>ignore
dock.Children.Add(menu) |>ignore

// Create tool bar
let tool = new ToolBar()
tool.Header <- "Toolbar"
DockPanel.SetDock(tool,Dock.Top) |>ignore
dock.Children.Add(tool) |>ignore

// Create Status bar
let status = new StatusBar()
let statitem = new StatusBarItem()
statitem.Content <- "Status"
status.Items.Add(statitem) |> ignore

// Dock status bar at bottom of panel
DockPanel.SetDock(status,Dock.Bottom) |>ignore
dock.Children.Add(status) |>ignore

// Create list box
let lstbox = new ListBox()
lstbox.Items.Add("List Box Item") |> ignore
DockPanel.SetDock(lstbox,Dock.Left) |>ignore
dock.Children.Add(lstbox) |>ignore

// Create text box
let txtbox = new TextBox()
txtbox.AcceptsReturn <- true
dock.Children.Add(txtbox) |>ignore
txtbox.Focus() |>ignore
end

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

CalculateYourLife

#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.Controls
open System.Windows.Controls.Primitives
open System.Windows.Input
open System.Windows.Media

// Utility functions
let checkPluralize i =
match i with
| 0 -> ""
| 1 -> ""
| _ -> "s"

let dayoffset deltaDays (dtEnd:DateTime) =
if (deltaDays < 0) then
(deltaDays + DateTime.DaysInMonth(dtEnd.Year, 1 + (dtEnd.Month+10)%10),-1)
else
(deltaDays,0)

let monthoffset deltaMonth =
if (deltaMonth <0) then
(deltaMonth + 12,1)
else
(deltaMonth,0)

let dateoffset (dtBegin:DateTime) (dtEnd:DateTime) =
let (day,deltaMonth) = dayoffset (dtEnd.Day - dtBegin.Day) dtEnd
let (deltaMonth,deltaYear) = monthoffset (dtEnd.Month - dtBegin.Month + deltaMonth)
let year = dtEnd.Year - dtBegin.Year + deltaYear
let month = dtEnd.Month - dtEnd.Year + deltaMonth
(day,month,year)


(* From Chap 5 - CalculateYourLife.cs *)
type CalculateYourLife = class
inherit Window as base

val mutable txtboxBegin : TextBox
val mutable txtboxEnd : TextBox
val mutable lablLifeYears : Label

new () as this = {
txtboxBegin = null
txtboxEnd = null
lablLifeYears = null} then

this.Title <- "Calculate Your Life"
this.SizeToContent <- SizeToContent.WidthAndHeight
this.ResizeMode <- ResizeMode.CanMinimize

let grid = new Grid()
this.Content <- grid

let addRowDef i =
let rowdef = new RowDefinition()
rowdef.Height <- GridLength.Auto
grid.RowDefinitions.Add(rowdef) |> ignore

List.iter addRowDef [0..2]

let addColDef i =
let coldef = new ColumnDefinition()
coldef.Width <- GridLength.Auto
grid.ColumnDefinitions.Add(coldef) |> ignore

List.iter addColDef [0..2]

// First Label
let lbl = new Label()
lbl.Content <- "Begin Date: "
grid.Children.Add(lbl) |> ignore
Grid.SetRow(lbl,0)
Grid.SetColumn(lbl,0)

// First TextBox
this.txtboxBegin <- new TextBox()
let begindate = new DateTime(1980,1,1)
this.txtboxBegin.Text <- begindate.ToShortDateString()

// Programming with out parameters!
this.txtboxBegin.TextChanged.Add(fun _ -> this.TextBoxOnTextChanged)
grid.Children.Add(this.txtboxBegin) |> ignore
Grid.SetRow(this.txtboxBegin, 0)
Grid.SetColumn(this.txtboxBegin, 1)

// Second Label
let lbl = new Label()
lbl.Content <- "End Date: "
grid.Children.Add(lbl) |> ignore
Grid.SetRow(lbl, 1)
Grid.SetColumn(lbl, 0)


// Second TextBox
this.txtboxEnd <- new TextBox()

// Programming with out parameters!
this.txtboxEnd.TextChanged.Add(fun _ -> this.TextBoxOnTextChanged)
grid.Children.Add(this.txtboxEnd) |> ignore
Grid.SetRow(this.txtboxEnd, 1)
Grid.SetColumn(this.txtboxEnd, 1)


// Third Label
let lbl = new Label()
lbl.Content <- "Life Years: "
grid.Children.Add(lbl) |> ignore
Grid.SetRow(lbl, 2)
Grid.SetColumn(lbl, 0)

// Label for calculated result
this.lablLifeYears <- new Label()
grid.Children.Add( this.lablLifeYears) |> ignore
Grid.SetRow( this.lablLifeYears, 2)
Grid.SetColumn( this.lablLifeYears, 1)

// Set margin for everybody
let thick = new Thickness(5.0)
grid.Margin <- thick

IEnumerable.untyped_to_typed grid.Children
|> IEnumerable.iter (fun (ctrl:Control) -> ctrl.Margin <- thick)

this.txtboxBegin.Focus() |>ignore

member this.TextBoxOnTextChanged =
// Use tuples to deal with C# out parameters...
let (begflag,dtBeg) = DateTime.TryParse(this.txtboxBegin.Text)
let (endflag,dtEnd) = DateTime.TryParse(this.txtboxEnd.Text)

match (begflag && endflag) with
| true ->
let (day,month,year) = dateoffset dtBeg dtEnd
// Replaced String.Format with Printf.sprintf...
let text = Printf.sprintf "%i year%s, %i month%s, %i day%s"
year (checkPluralize year)
month (checkPluralize month)
day (checkPluralize day)
this.lablLifeYears.Content <- text
| false -> this.lablLifeYears.Content <- ""

end

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

EnterTheGrid

#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.Controls
open System.Windows.Input
open System.Windows.Media

// Copied the zip & zipWith function from Haskell Prelude
// In the initial version, I forgot about pattern matching
// head/tail with head::tail. Revisiting the example codes
// from Don Syme's upcoming book refreshed my memory.
let rec zipWith func a b =
match (a,b) with
| (ha::ta),(hb::tb) -> [func ha hb] @ (zipWith func ta tb)
| _ -> []

// Curried function to create pairs of lists
let zip = zipWith (fun x y -> (x,y))

(* From Chap 5 - EnterTheGrid.cs *)
type EnterTheGrid = class
inherit Window as base

new () as this = {} then
this.Title <- "Enter the Grid"
this.MinWidth <- 300.0
this.SizeToContent <- SizeToContent.WidthAndHeight

// Create StackPanel for window content
let stack = new StackPanel()
this.Content <- stack

// Create Grid and add to StackPanel
let grid1 = new Grid()
grid1.Margin <- new Thickness(5.0)
stack.Children.Add(grid1)|>ignore

let addRow i =
let rowdef = new RowDefinition()
rowdef.Height <- GridLength.Auto
grid1.RowDefinitions.Add(rowdef)

List.iter addRow [0..4]

let coldef = new ColumnDefinition()
coldef.Width <- GridLength.Auto
grid1.ColumnDefinitions.Add(coldef)

let coldef = new ColumnDefinition()
coldef.Width <- new GridLength(100.0,GridUnitType.Star)
grid1.ColumnDefinitions.Add(coldef)

let strLabels = ["_First name:"
"_Last name:"
"_Social security number:"
"_Credit card number:"
"_Other personal stuff:"]

let addLabel (i,label) =
let lbl = new Label()
lbl.Content <- label
lbl.VerticalContentAlignment <- VerticalAlignment.Center
grid1.Children.Add(lbl) |>ignore
Grid.SetRow(lbl,i)
Grid.SetColumn(lbl,0)

let txtbox = new TextBox()
txtbox.Margin <- new Thickness(5.0)
grid1.Children.Add(txtbox)|>ignore
Grid.SetRow(txtbox,i)
Grid.SetColumn(txtbox,1)

// Using zip to create the pair argument needed for addLabel
List.iter addLabel (zip [0..(strLabels.Length-1)] strLabels)

// Create second grid and add to StackPanel
let grid2 = new Grid()
grid2.Margin <- new Thickness(10.0)
stack.Children.Add(grid2) |> ignore

// No row definitions needed for single row
// Default column definitions are "star"
grid2.ColumnDefinitions.Add(new ColumnDefinition())
grid2.ColumnDefinitions.Add(new ColumnDefinition())

// Create buttons
let addButton label =
let btn = new Button()
btn.Content <- label
btn.HorizontalAlignment <- HorizontalAlignment.Center
btn.IsDefault <- true;
btn.Click.Add(fun _ -> this.Close())
grid2.Children.Add(btn) |> ignore
btn

ignore(addButton "Submit")
let btn = addButton "Cancel"
Grid.SetColumn(btn,1)

// Set focus to first text box
let p = stack.Children.Item(0) :?> Panel in
p.Children.Item(1).Focus() |> ignore
end

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

SpanTheCells

#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.Controls
open System.Windows.Input
open System.Windows.Media

let rec zipWith func a b =
match (a,b) with
| (ha::ta),(hb::tb) -> [func ha hb] @ (zipWith func ta tb)
| _ -> []

// Curried function to create pairs of lists
let zip = zipWith (fun x y -> (x,y))


(* From Chap 5 - SpanTheCells.cs *)
type SpanTheCells = class
inherit Window as base

new () as this = {} then
this.Title <- "Span the Cells"
this.MinWidth <- 300.0
this.SizeToContent <- SizeToContent.WidthAndHeight

let grid = new Grid()
grid.Margin <- new Thickness(5.0)
this.Content <- grid

for i = 0 to 6 do
let rowdef = new RowDefinition()
rowdef.Height <- GridLength.Auto
grid.RowDefinitions.Add(rowdef)

let addColumn i =
let coldef = new ColumnDefinition()
if (i=1) then
coldef.Width <- new GridLength(100.0,GridUnitType.Star)
else
coldef.Width <- GridLength.Auto
grid.ColumnDefinitions.Add(coldef)


List.iter addColumn [0..3]

let astrLabel = ["_First name:"
"_Last name:"
"_Social security number:"
"_Credit card number:"
"_Other personal stuff:"]

let addRow (i,label) =
let lbl = new Label()
lbl.Content <- label
lbl.VerticalContentAlignment <- VerticalAlignment.Center
grid.Children.Add(lbl) |>ignore
Grid.SetRow(lbl, i)
Grid.SetColumn(lbl,0)

let txtbox = new TextBox()
txtbox.Margin <- new Thickness(5.0)
grid.Children.Add(txtbox) |> ignore
Grid.SetRow(txtbox,i)
Grid.SetColumn(txtbox,1)
Grid.SetColumnSpan(txtbox,3)

List.iter addRow (zip [0..(astrLabel.Length -1)] astrLabel)

let btn = new Button()
btn.Content <- "Submit"
btn.Margin <- new Thickness(5.0)
btn.IsDefault <- true
btn.Click.Add(fun _ -> this.Close()) |>ignore
grid.Children.Add(btn) |> ignore
Grid.SetRow(btn,5)
Grid.SetColumn(btn,2)

let btn = new Button()
btn.Content <- "Cancel"
btn.Margin <- new Thickness(5.0)
btn.IsCancel <- true
btn.Click.Add(fun _ -> this.Close()) |>ignore
grid.Children.Add(btn) |> ignore
Grid.SetRow(btn,5)
Grid.SetColumn(btn,3)

grid.Children.[1].Focus() |> ignore

end

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

SplitNine

#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.Controls
open System.Windows.Input
open System.Windows.Media

(* From Chap 5 - SplitNine.cs *)
type SplitNine = class
inherit Window as base

new () as this = {} then
this.Title <- "Split Nine"

let grid = new Grid()
this.Content <- grid

for i = 0 to 3 do
grid.ColumnDefinitions.Add(new ColumnDefinition())
grid.RowDefinitions.Add(new RowDefinition())

let createButton (x,y) =
let btn = new Button()
btn.Content <- "Row " + Int32.to_string(y) + " and Column " + Int32.to_string(x)
btn.Margin <- new Thickness(20.0)
grid.Children.Add(btn)|> ignore
Grid.SetRow(btn,y)
Grid.SetColumn(btn,x)

let createSplitter =
let split = new GridSplitter()
split.Width <- 6.0
grid.Children.Add(split) |>ignore
Grid.SetRow(split,1)
Grid.SetColumn(split,1)
Grid.SetRowSpan(split,3)

for x = 0 to 2 do
for y = 0 to 2 do
createButton (x,y)
createSplitter


end

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

SplitTheClient

#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.Controls
open System.Windows.Input
open System.Windows.Media

let addgridcol (grid:Grid) =
grid.ColumnDefinitions.Add(new ColumnDefinition())
grid.RowDefinitions.Add(new RowDefinition())


(* From Chap 5 - SplitTheClient.cs *)
type SplitTheClient = class
inherit Window as base

new () as this = {} then
this.Title <- "Split the Client"


// Grid with vertical splitter
let grid1 = new Grid()
addgridcol grid1
addgridcol grid1
addgridcol grid1

grid1.ColumnDefinitions.[1].Width <- GridLength.Auto
this.Content <- grid1

// Button at the left of the vertical splitter
let btn = new Button()
btn.Content <- "Button No. 1"
grid1.Children.Add(btn) |> ignore
Grid.SetRow(btn,0)
Grid.SetColumn(btn,0)

//Vertical Splitter
let split = new GridSplitter()
split.ShowsPreview <- true
split.HorizontalAlignment <- HorizontalAlignment.Center
split.VerticalAlignment <- VerticalAlignment.Stretch
split.Width <- 6.0
grid1.Children.Add(split)|>ignore
Grid.SetRow(split,0)
Grid.SetColumn(split,0)

let grid2 = new Grid()
addgridcol grid2
addgridcol grid2
addgridcol grid2
grid2.RowDefinitions.[1].Height <- GridLength.Auto
grid1.Children.Add(grid2) |>ignore
Grid.SetRow(grid2, 0);
Grid.SetColumn(grid2, 2)

let btn = new Button();
btn.Content <- "Button No. 2";
grid2.Children.Add(btn) |>ignore
Grid.SetRow(btn, 0)
Grid.SetColumn(btn, 0)

// Horizontal splitter.
let split = new GridSplitter()
split.ShowsPreview <- true;
split.HorizontalAlignment <- HorizontalAlignment.Stretch;
split.VerticalAlignment <- VerticalAlignment.Center;
split.Height <- 6.0;
grid2.Children.Add(split) |> ignore
Grid.SetRow(split, 1)
Grid.SetColumn(split, 0)

// Bottom at bottom of horizontal splitter.
let btn = new Button();
btn.Content <- "Button No. 3";
grid2.Children.Add(btn) |> ignore
Grid.SetRow(btn, 2);
Grid.SetColumn(btn, 0);
end

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

ScrollCustomColors

#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.Controls
open System.Windows.Controls.Primitives
open System.Windows.Media


(* From Chap 6 - ScrollCustomColors.cs *)
type ColorScroll = class
inherit Window as base

val mutable pnlColor : Panel
val mutable scrolls : ScrollBar array

new () as this = {
pnlColor = (new StackPanel() :> Panel)
scrolls = Array.create 3 (new ScrollBar()) } then
this.Title <- "Color Scroll"
this.Width <- 500.0
this.Height <- 300.0

// GridMain contains a vertical splitter
let gridMain = new Grid()
this.Content <- gridMain

// GridMain column definitions
let coldef = new ColumnDefinition()
coldef.Width <- new GridLength(200.0,GridUnitType.Pixel)
gridMain.ColumnDefinitions.Add(coldef)

let coldef = new ColumnDefinition()
coldef.Width <- GridLength.Auto
gridMain.ColumnDefinitions.Add(coldef)

let coldef = new ColumnDefinition()
coldef.Width <- new GridLength(100.0,GridUnitType.Star)
gridMain.ColumnDefinitions.Add(coldef)

// Vertical splitter
let split = new GridSplitter()
split.HorizontalAlignment <- HorizontalAlignment.Center

split.VerticalAlignment <- VerticalAlignment.Stretch
split.Width <- 6.0
gridMain.Children.Add(split)|>ignore
Grid.SetRow(split,0)
Grid.SetColumn(split,1)

// Panel on right side of splitter to display color
this.pnlColor.Background <- new SolidColorBrush(SystemColors.WindowColor)
gridMain.Children.Add(this.pnlColor)|>ignore
Grid.SetRow(this.pnlColor,0)
Grid.SetColumn(this.pnlColor,2)

// Secondary grid at left of splitter
let grid = new Grid()
gridMain.Children.Add(grid)|>ignore
Grid.SetRow(grid,0)
Grid.SetColumn(grid,0)

// Three rows for label, scroll, and label
let rowdef = new RowDefinition()
rowdef.Height <- GridLength.Auto
grid.RowDefinitions.Add(rowdef)

let rowdef = new RowDefinition()
rowdef.Height <- new GridLength(100.0, GridUnitType.Star)
grid.RowDefinitions.Add(rowdef)

let rowdef = new RowDefinition()
rowdef.Height <- GridLength.Auto
grid.RowDefinitions.Add(rowdef)

// Three columns for Red, Green, and Blue.
for i in [0..2] do
let coldef = new ColumnDefinition()
coldef.Width <- new GridLength(33.0, GridUnitType.Star)
grid.ColumnDefinitions.Add(coldef)

let clr = (this.pnlColor.Background :?> SolidColorBrush).Color
let clrText = [|"Red";"Green";"Blue"|]
for i in [0..2] do
let lbl = new Label();
lbl.Content <- clrText.[i]
lbl.HorizontalAlignment <- HorizontalAlignment.Center;
grid.Children.Add(lbl) |>ignore
Grid.SetRow(lbl, 0)
Grid.SetColumn(lbl, i)

this.scrolls.[i] <- new ScrollBar()
this.scrolls.[i].Focusable <- true
this.scrolls.[i].Orientation <- Orientation.Vertical
this.scrolls.[i].Minimum <- 0.0
this.scrolls.[i].Maximum <- 255.0
this.scrolls.[i].SmallChange <- 1.0
this.scrolls.[i].LargeChange <- 16.0

// Implemented ScrollOnValueChanged as lambda expression
this.scrolls.[i].ValueChanged.Add
(fun args ->
let scroll = this.scrolls.[i]
let pnl = scroll.Parent :?> Panel
let txt = pnl.Children.[1 + pnl.Children.IndexOf(scroll)] :?> TextBlock
txt.Text <- String.Format("{0}\n0x{0:X2}", Int32.of_float(scroll.Value))
this.pnlColor.Background <-
new SolidColorBrush
(Color.FromRgb
(Byte.of_int32(Float.to_int32(this.scrolls.[0].Value)),
Byte.of_int32(Float.to_int32(this.scrolls.[1].Value)),
Byte.of_int32(Float.to_int32(this.scrolls.[2].Value)))))

grid.Children.Add(this.scrolls.[i]) |>ignore
Grid.SetRow(this.scrolls.[i], 1)
Grid.SetColumn(this.scrolls.[i], i)

let txtValue = new TextBlock() in
txtValue.TextAlignment <- TextAlignment.Center
txtValue.HorizontalAlignment <- HorizontalAlignment.Center
txtValue.Margin <- new Thickness(5.0);
grid.Children.Add(txtValue) |> ignore
Grid.SetRow(txtValue, 2)
Grid.SetColumn(txtValue, i)

this.scrolls.[0].Value <- Float.of_int32(Byte.to_int32(clr.R))
this.scrolls.[1].Value <- Float.of_int32(Byte.to_int32(clr.G))
this.scrolls.[2].Value <- Float.of_int32(Byte.to_int32(clr.B))

this.scrolls.[0].Focus() |> ignore

end

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


4 comments:

bluestorm said...

> [func ha hb] @ (zipWith func ta tb)

func ha hb :: zipWith func ta tb

Moreover, zipWith already exists in OCaml standard library : List.map2. Seems it is in Microsoft.FSharp.Collections.List too.

john said...

Thanks for pointing that out. I somehow missed the fact that F# have zip already in List.zip.

wmeyer said...

The getdock function in the first example could be simplified like this:

let getdock i = enum<Dock> i

(But I don't know whether this syntax was available in pre-2.0 F#.)

john said...

Thanks! That's not an idiom I'm familiar with so I appreciate you pointing it out.