Friday, October 26, 2007

Learning WPF

I'm trying to learn about Microsoft's Windows Presentation Foundation and I am going through the book "Applications = Code + Markup" by Charles Petzold. Usually for me to learn something like this, I would type out the code, which slows my reading down enough for me to ruminate on the code. But typing the code verbatim is sometimes a mind numbing exercise. So I decided to rewrite the C# code in F# instead. This helps learn F# while at the same time keeps me engaged in learning WPF. I'm going to start with Chapter 1 and work my way thru the chapters and see how far I get before I get tired of this exercise.

Why did I pick F# as the target programming language to translate the code to? Well, which the advent of multicore processors, functional programming languages seems naturally suited to the new hardware architectures. In addition, Wall Street Journal made a passing reference to F# in the article Behind Microsoft's Bid to Gain Cutting Edige. I figure if Wall Street Journal have an article about F#, it seems about time that F# will become mainstream. Having two F# books coming out also helps developers to start learning about this language by Microsoft.

Here are the F# versions of Charles Petzold's code for Chapter 1:


SayHello.cs


#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

(* From Chap 1 - SayHello.cs *)
let win = new Window()
win.Title <- "Say Hello"
win.Show()

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


HandleAnEvent.cs


#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.Input

(* From Chap 1 - HandleAnEvent.cs *)
let WindowOnMouseDown sender (args:MouseButtonEventArgs) =
let win = unbox<Window> sender
let str = string.Format("Window clicked with {0} button at point ({1})",args.ChangedButton,args.GetPosition(win))
MessageBox.Show(str)|>ignore

let win = new Window()
win.Title <- "Handle an Event"
win.add_MouseDown(new MouseButtonEventHandler(WindowOnMouseDown))


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

InheritTheApp.cs


#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.Input

(* From Chap 1 - InheritTheApp.cs *)
type InheritTheApp = class
inherit Application as base

// No constructor
new () as this = {}

override this.OnStartup (args:StartupEventArgs) =
base.OnStartup(args)
let win = new Window()
win.Title <- "Inherit the App"
win.Show()

override this.OnSessionEnding (args:SessionEndingCancelEventArgs) =
base.OnSessionEnding(args)
let result = MessageBox.Show("Do you want to save your data?", base.MainWindow.Title, MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.Yes)
args.Cancel <- (result = MessageBoxResult.Cancel)

end

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

MyWindow.cs + MyApplication.cs +InheritAppAndWindowApp.cs


#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.Input

(* From Chap 1 - MyWindow.cs *)
type MyWindow = class
inherit Window as base

new () as this = {} then
this.Title <- "Inherit App & Window "

override this.OnMouseDown (args:MouseButtonEventArgs) =
base.OnMouseDown(args)

let strMessage = string.Format("Window clicked with {0} button at point ({1})",args.ChangedButton,args.GetPosition(this))
MessageBox.Show(strMessage,this.Title) |> ignore
end

(* From Chap 1 - MyApplication.cs *)
type MyApplication = class
inherit Application as base

// No constructor
new () as this = {}

override this.OnStartup (args:StartupEventArgs) =
base.OnStartup(args)

let win = new MyWindow()
win.Show()
end

(* From Chap 1 - InheritAppAndWindow.cs *)
#if COMPILED
[<STAThread()>]
do
let app = new MyApplication() in
app.Run() |> ignore
#endif


GrowAndShrink.cs


#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.Input

(* From Chap 1 - GrowAndShrink.cs *)
type GrowAndShrink = class
inherit Window as base

new () as this = {} then
this.Title <- "Grow & Shrink"
this.WindowStartupLocation <- WindowStartupLocation.CenterScreen
this.Width <- 192.0
this.Height <- 192.0

override this.OnKeyDown (args:KeyEventArgs) =
base.OnKeyDown(args)
if (args.Key = Key.Up) then
this.Left <- this.Left - 0.05 * this.Width
this.Top <- this.Top - 0.05 * this.Height
this.Width <- this.Width * 1.1
this.Height <- this.Height * 1.1
else if (args.Key = Key.Down) then
this.Left <- this.Left + 0.05 * (this.Width/1.1)
this.Top <- this.Top + 0.05 * (this.Height/1.1)
this.Width <- this.Width/1.1
this.Height <- this.Height /1.1


end


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


TypeYourTitle.cs


#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.Input

(* From Chap 1 - TypeYourTitle.cs *)
type TypeYourTitle = class
inherit Window as base

new () as this = {}

override this.OnTextInput (args:TextCompositionEventArgs) =
base.OnTextInput(args)
if (args.Text = "\b" && this.Title.Length > 0) then
this.Title <- this.Title.Substring(0,this.Title.Length -1)
// Change > operator to >= operator or what you type will not show up
else if (this.Title.Length >= 0 && (not (Char.IsControl(String.get args.Text 0)) )) then
this.Title <- this.Title + args.Text


end


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

5 comments:

Josh Smith said...

Excellent job, John! :)

I do not understand why every sample has the #if COMPILED ... #endif wrapping a main(). Why did you do that?

Thanks,
Josh Smith

John said...

Hi Josh,

This is explained in the excellent book by Don Syme in Chapter 7 of his book "Expert F#". Basically, this allows me to run the code in interactive mode. Only in compilation mode, does the code include the main executable.

Regards,
John

dondublon said...

Whell, the line

inherit Window as base

couldn't be compiled, error in "as base"

The MSDN speaks nothing about "as base" construction, too.

John Liao said...

Hi dondublon,

F# has changed significantly since I first started these blogs. Back then, base was not a F# keyword. It is now a F# keyword. There was nothing special about "base", it was just an alias. You could have used "x" and and later in the code reference that as "x.OnMouseDown(args)", but F# has changed such that you can refer to base object as "base.OnMouseDown(args)" without the "as " phrase anymore. Feel free to remove the "as base" phrase for the latest version of F#.

Regards,
John

dondublon said...

Yes, you right.
sorry for my incompetence.