Friday, November 30, 2007

Playing around with Workflow Foundation in F#

I am also investigating Workflow Foundation. While workflow foundation is made a lot easier with appropriate GUI tool support, I still find it instructive to implement some examples in F# and see how it works. Here's a very simplistic workflow example impleted in F#:

#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"System.Workflow.Activities.dll"
#r @"System.Workflow.ComponentModel.dll"
#r @"System.Workflow.Runtime.dll"

open System
open System.Collections
open System.Threading
open System.Workflow.Activities
open System.Workflow.ComponentModel
open System.Workflow.Runtime
//
// Build a simple workflow
//
type Workflow1 = class
inherit SequentialWorkflowActivity as base

val mutable createMessage : CodeActivity
val mutable firstName : string
val mutable message : string

new() as this = {createMessage = null;firstName=null;message=null} then
this.InitializeComponent()

member this.FirstName with set value = this.firstName <- value

member this.Message with get() = this.message

(* Define workflow task *)
member this.composeMessage sender (e:EventArgs) =
this.message <- "Hello " + this.firstName + "!"

(* Normally, Visual Studio would generate this with the help of GUI tools *)
member this.InitializeComponent () =
this.CanModifyActivities <- true
this.createMessage <- new System.Workflow.Activities.CodeActivity()

// create Message
this.createMessage.Name <- "createMessage"
this.createMessage.ExecuteCode.AddHandler
(new System.EventHandler(this.composeMessage))


// Workflow1
this.Activities.Add(this.createMessage)
this.Name <- "Workflow1"
this.CanModifyActivities <- false

end
//
// Workflow Engine Hosting Code
//
let main =

using (new WorkflowRuntime()) (fun wfRuntime ->
let waitHandle = new AutoResetEvent(false)

wfRuntime.WorkflowCompleted.Add
(fun (e:WorkflowCompletedEventArgs) ->
let message = e.OutputParameters.["Message"]
Console.WriteLine(message)
waitHandle.Set() |> ignore)

wfRuntime.WorkflowTerminated.Add
(fun (e:WorkflowTerminatedEventArgs) ->
Console.WriteLine(e.Exception.Message)
waitHandle.Set()|>ignore)

let inparam = new Generic.Dictionary<string, obj>()
inparam.Add("FirstName",box("Joe"))

let instance = wfRuntime.CreateWorkflow(typeof<Workflow1>,inparam)
instance.Start() |> ignore
waitHandle.WaitOne() |> ignore
Console.WriteLine("Done!")
)

// Execute the workflow
do main

Wednesday, November 28, 2007

Learning WPF with F# - Canvas

Examples from Chapter 7 of Petzold's book "Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation"


PaintTheButton

#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
open System.Windows.Shapes

(* From Chap 7 - PaintTheButton.cs *)
type PaintTheButton = class
inherit Window as base

new () as this = {} then
this.Title <- "Paint the Button"

// Create the Button as content of the window
let btn = new Button()
btn.HorizontalAlignment <- HorizontalAlignment.Center
btn.VerticalAlignment <- VerticalAlignment.Center
this.Content <- btn

// Create the Canvas as content of the button
let canv = new Canvas()
canv.Width <- 144.0
canv.Height <- 144.0
btn.Content <- canv

// Create Rectangle as child of canvas
let rect = new Rectangle()
rect.Width <- canv.Width
rect.Height <- canv.Height
rect.RadiusX <- 24.0
rect.RadiusY <- 24.0
rect.Fill <- Brushes.Blue;
canv.Children.Add(rect) |>ignore
Canvas.SetLeft(rect, 0.0)
Canvas.SetRight(rect, 0.0)

// Create Polygon as child of canvas
let poly = new Polygon()
poly.Fill <- Brushes.Yellow
poly.Points <- new PointCollection()

for i in [0..4] do
let angle = Int32.to_float(i)*4.0*Math.PI/5.0
let pt = new Point(48.0*Math.Sin(angle),-48.0*Math.Cos(angle))
poly.Points.Add(pt)

canv.Children.Add(poly) |>ignore
Canvas.SetLeft(poly, canv.Width / 2.0)
Canvas.SetTop(poly, canv.Height / 2.0)

end

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

PlayJeuDeTacquin

#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
open System.Windows.Shapes
open System.Windows.Threading
//
// From Chap 7 - PlayJeuDeTacquin example
//
(* From Chap 7 - Tile.cs *)
type Tile = class
inherit Canvas as base

static member SIZE = 64.0
static member BORD = 6.0
val mutable txtblk : TextBlock

new () as this = {txtblk=null} then
this.Width <- Tile.SIZE
this.Height <- Tile.SIZE

// Upper-left shadowed border
let poly = new Polygon()
poly.Points <- new PointCollection
([|new Point(0.0,0.0)
new Point(Tile.SIZE,0.0)
new Point(Tile.SIZE-Tile.BORD,Tile.BORD)
new Point(Tile.BORD,Tile.BORD)
new Point(Tile.BORD,Tile.SIZE-Tile.BORD)
new Point(0.0,Tile.SIZE)|])
poly.Fill <- SystemColors.ControlLightLightBrush
this.Children.Add(poly)|>ignore

// Lower-right shadowed border
let poly = new Polygon()
poly.Points <- new PointCollection
([|new Point(Tile.SIZE,Tile.SIZE)
new Point(Tile.SIZE,0.0)
new Point(Tile.SIZE-Tile.BORD,Tile.BORD)
new Point(Tile.SIZE-Tile.BORD,Tile.SIZE-Tile.BORD)
new Point(Tile.BORD,Tile.SIZE-Tile.BORD)
new Point(0.0,Tile.SIZE)|])
poly.Fill <- SystemColors.ControlDarkBrush
this.Children.Add(poly)|>ignore

// Host for centered text
let bord = new Border()
bord.Width <- Tile.SIZE - 2.0 * Tile.BORD
bord.Height <- Tile.SIZE - 2.0 * Tile.BORD
bord.Background <- SystemColors.ControlBrush;
this.Children.Add(bord) |>ignore
Canvas.SetLeft(bord, Tile.BORD)
Canvas.SetTop(bord, Tile.BORD)

// Display of text
this.txtblk <- new TextBlock()
this.txtblk.FontSize <- 32.0
this.txtblk.Foreground <- SystemColors.ControlTextBrush
this.txtblk.HorizontalAlignment <- HorizontalAlignment.Center
this.txtblk.VerticalAlignment <- VerticalAlignment.Center
bord.Child <- this.txtblk

// Public property to set text
member this.Text
with get () = this.txtblk.Text
and set value = this.txtblk.Text <- value

end

(* From Chap 7 - Empty.cs *)
type Empty = class
inherit System.Windows.FrameworkElement as base

new () as this = {}

end

(* From Chap 7 - PlayJeuDeTacquin.cs *)
type PlayJeuDeTacquin = class
inherit Window as base

// Define constant member variables
member this.NumberRows = 4
member this.NumberCols = 4

val mutable unigrid : UniformGrid
val mutable xEmpty : int
val mutable yEmpty : int
val mutable iCounter : int
val mutable keys : Key array
val mutable elEmptySpare : UIElement


new () as this = {
unigrid=null
xEmpty = 0
yEmpty = 0
iCounter = 0
keys = [|Key.Left;Key.Right;Key.Down|]
elEmptySpare = ((new Empty()) :> UIElement) } then
this.Title <- "Jeu de Tacquin";
this.SizeToContent <- SizeToContent.WidthAndHeight;
this.ResizeMode <- ResizeMode.CanMinimize;
this.Background <- SystemColors.ControlBrush;

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

// Create Button at top of window
let btn = new Button()
btn.Content <- "_Scramble"
btn.Margin <- new Thickness(10.0)
btn.HorizontalAlignment <- HorizontalAlignment.Center

// lambda expressions allow us to remove the need for member variable rand
btn.Click.Add
(fun _ ->
let rand = new Random()
this.iCounter <- 16 * this.NumberCols * this.NumberRows
let tmr = new DispatcherTimer()
tmr.Interval <- TimeSpan.FromMilliseconds(10.0)
tmr.Tick.Add
(fun e ->
for i in [0..4] do
this.MoveTile this.xEmpty (this.yEmpty + rand.Next(3) - 1)
this.MoveTile (this.xEmpty + rand.Next(3) - 1) this.yEmpty
this.iCounter <- this.iCounter -1
if (this.iCounter = 0) then tmr.Stop())
tmr.Start())

stack.Children.Add(btn) |>ignore

// Create Border for aesthetic purposes.
let bord = new Border()
bord.BorderBrush <- SystemColors.ControlDarkDarkBrush
bord.BorderThickness <- new Thickness(1.0)
stack.Children.Add(bord) |>ignore

// Create Unigrid as Child of Border.
this.unigrid <- new UniformGrid()
this.unigrid.Rows <- this.NumberRows
this.unigrid.Columns <- this.NumberCols
bord.Child <- this.unigrid

for i in [1..(this.NumberRows*this.NumberCols-1)] do
let tile = new Tile()
tile.Text <- Int32.to_string(i)

tile.MouseLeftButtonDown.Add
(fun e ->
let iMove = this.unigrid.Children.IndexOf(tile)
let xMove = iMove % this.NumberCols
let yMove = iMove / this.NumberCols

if (xMove = this.xEmpty) then
while (yMove <> this.yEmpty) do
let yTarget = this.yEmpty +
(yMove - this.yEmpty)/Math.Abs(yMove -this.yEmpty)
this.MoveTile xMove yTarget

if (yMove = this.yEmpty) then
while (xMove <> this.xEmpty) do
let xTarget = this.xEmpty +
(xMove - this.xEmpty)/Math.Abs(xMove -this.xEmpty)
this.MoveTile xTarget yMove
())
this.unigrid.Children.Add(tile) |> ignore

this.unigrid.Children.Add(new Empty()) |>ignore
this.xEmpty <- this.NumberCols -1
this.yEmpty <- this.NumberRows -1

member this.MoveTile (xTile:int) (yTile:int) =
if ((xTile = this.xEmpty && yTile = this.yEmpty)
|| xTile < 0 || xTile >= this.NumberCols
|| yTile < 0 || yTile >= this.NumberRows) then
()
else
let iTile = this.NumberCols * yTile + xTile
let iEmpty = this.NumberCols * this.yEmpty + this.xEmpty
let elTile = this.unigrid.Children.[iTile]
let elEmpty = this.unigrid.Children.[iEmpty]

this.unigrid.Children.RemoveAt(iTile)
this.unigrid.Children.Insert(iTile, this.elEmptySpare)
this.unigrid.Children.RemoveAt(iEmpty)
this.unigrid.Children.Insert(iEmpty, elTile)
this.xEmpty <- xTile
this.yEmpty <- yTile
this.elEmptySpare <- elEmpty

end

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


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