Friday, December 07, 2007

Working with F# - DependencyProperty and static readonly field workaround

In my last blog entry, I talked about how I had problems implementing the DependencyProperty in both SpaceButton and SpaceWindow class. After sleeping on it, I figured out a way to workaround that. I was about to post my workaround to my blog only to find that Dr. Don Syme has already posted a comment with the workaround to my blog already! Thanks! I am honored that he's even taken the time to read my blog.

By the way, I recently purchased a copy of Expert F# (I'm kind of surprised that Expert F# is published by Apress instead of Microsoft Press) and slowly reading through the book. While I only finished the first couple chapters, I heartily recommend this book for anyone who wants to learn F#. It certainly made clearer many fuzzy notions that I have of the F# language and introduce me to features that I did not know exist such as option values.

I'm reposting Don Syme's solutions below adding color syntax highlighting...


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

open System
open System.Text
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
// Workaround solution from Don Syme!
(* From Chap 8 - SetSpaceProperty example with DependencyProperty *)
//
(* From Chap 8 - SpaceButton.cs *)
// Workaround solution
let mutable private initSpaceButtonSpaceProperty : DependencyProperty = null
let mutable private initSpaceWindowSpaceProperty : DependencyProperty = null
//
// Switched to implicit construction for classes
type SpaceButton() =
inherit Button() as base

let mutable txt = null

static member SpaceProperty =
if initSpaceButtonSpaceProperty = null then
let metadata = new FrameworkPropertyMetadata(DefaultValue=1,
AffectsMeasure=true,
Inherits=true)
metadata.PropertyChangedCallback <- new PropertyChangedCallback
(SpaceButton.OnSpacePropertyChanged)

initSpaceButtonSpaceProperty <-
DependencyProperty.Register
("Space",
typeof<int>,
typeof<SpaceButton>,
metadata,
// callback method for value validation
(fun obj -> let i = (obj :?> int) in (i >= 0)))

initSpaceButtonSpaceProperty

static member OnSpacePropertyChanged (obj:DependencyObject) (args:DependencyPropertyChangedEventArgs) =
let btn = obj :?> SpaceButton
btn.Content <- btn.SpaceOutText btn.Text

member this.Text
with get() = txt
and set value =
txt <- value
this.Content <- this.SpaceOutText(txt)

member this.Space
with get() =
let value = this.GetValue(SpaceButton.SpaceProperty)
(value :?> int)
and set (value:int) = this.SetValue(SpaceButton.SpaceProperty,value)

member this.SpaceOutText (str:string) =
if (str <> null) then
let appendSpace c = String.of_char(c) + new string(' ',this.Space)
let build = String.map_concat appendSpace str
build
else
null


(* From Chap 8 - SpaceWindow.cs *)
// Switched to implicit construction for classes
type SpaceWindow() =
inherit Window() as base

// A static DependencyProperty
static member SpaceProperty =
if initSpaceWindowSpaceProperty = null then
let metadata = new FrameworkPropertyMetadata()
metadata.Inherits <- true

// Add owner to SpaceProperty & override metadata
initSpaceWindowSpaceProperty <- SpaceButton.SpaceProperty.AddOwner(typeof<SpaceWindow>)
initSpaceWindowSpaceProperty.OverrideMetadata(typeof<SpaceWindow>,metadata)
// returns the SpaceProperty value
initSpaceWindowSpaceProperty

member this.Space
with get() = (this.GetValue(SpaceWindow.SpaceProperty) :?> int)
and set (value:int) = this.SetValue(SpaceWindow.SpaceProperty,value)


(* From Chap 8 - SetSpaceProperty.cs *)
// Switched to implicit construction for classes
type SetSpaceProperty() as this =
inherit SpaceWindow() as base

// I did not know you can do this until Don Syme showed me this...
do this.Title <- "Set Space Property"
this.SizeToContent <- SizeToContent.WidthAndHeight
this.ResizeMode <- ResizeMode.CanMinimize
let iSpaces = [|0;1;2|]

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

for i in [0..2] do
let row = new RowDefinition()
row.Height <- GridLength.Auto
grid.RowDefinitions.Add(row)

for i in [0..(iSpaces.Length-1)] do
let col = new ColumnDefinition()
col.Width <- GridLength.Auto
grid.ColumnDefinitions.Add(col)

for i in [0..(iSpaces.Length-1)] do
let btn = new SpaceButton(Text="Set window Space to " + Int32.to_string(iSpaces.[i]),
Tag=iSpaces.[i],
HorizontalAlignment=HorizontalAlignment.Center,
VerticalAlignment=VerticalAlignment.Center)

//btn.Click += WindowPropertyOnClick;
btn.Click.Add(fun _ -> this.Space <- (btn.Tag :?> int))
grid.Children.Add(btn)|>ignore
Grid.SetRow(btn, 0)
Grid.SetColumn(btn, i)


let btn = new SpaceButton(Text= "Set button Space to " + Int32.to_string(iSpaces.[i]),
Tag=iSpaces.[i],
HorizontalAlignment=HorizontalAlignment.Center,
VerticalAlignment=VerticalAlignment.Center)

//btn.Click += ButtonPropertyOnClick
btn.Click.Add(fun _ -> btn.Space <- (btn.Tag :?> int))
grid.Children.Add(btn) |> ignore
Grid.SetRow(btn, 1)
Grid.SetColumn(btn, i)


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

No comments: