Working through Chapter 8 of Petzold's "Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation" book.
Working on the SpaceButton examples forced me to work with static members and static constructors in F#. Unfortunately, I'm completely stumped. In the SpaceButton example, I don't know how to setup a static readonly field in F# and I don't know how to initialize SpaceProperty outside of SpaceButton class due to initialization dependency with SpaceButton. The closest thing that I can find on the web is the following blog entry by Lewis Bruck which indicates that F# and SQL2005 CLR has problems because F# does not generate static readonly fields. Don Syme apparently has thought about this subject as he has published an article An Alternative Approach to Initializing Mutually Referential Objects". But it wasn't obvious to me how to resolve my problem with initializing SpaceProperty field. I post the code I have so far, but it's nonfunctional. If anyone else knows a solution, please let me know!
SetFontSizeProperty
#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.Documents
open System.Windows.Input
open System.Windows.Media
(* From Chap 8 - SetFontSizeProperty.cs *)
type SetFontSizeProperty = class
inherit Window as base
new () as this = {} then
this.Title <- "Set FontSize Property";
this.SizeToContent <- SizeToContent.WidthAndHeight;
this.ResizeMode <- ResizeMode.CanMinimize;
this.FontSize <- 16.0
let fntsizes = [|8.0;16.0;32.0|]
// Create Grid Panel
let grid = new Grid()
this.Content <- grid
// Define row and columns
for i in [0..1] do
let row = new RowDefinition()
row.Height <- GridLength.Auto
grid.RowDefinitions.Add(row)
for i in [0..(fntsizes.Length-1)] do
let col = new ColumnDefinition()
col.Width <- GridLength.Auto
grid.ColumnDefinitions.Add(col)
// Create six buttons
for i in [0..(fntsizes.Length-1)] do
let btn = new Button()
btn.Content <- new TextBlock
(new Run("Set window FontSize to " + Float.to_string(fntsizes.[i])))
btn.Tag <- fntsizes.[i]
btn.HorizontalAlignment <- HorizontalAlignment.Center
btn.VerticalAlignment <- VerticalAlignment.Center
// Implement WindowFontSizeOnClick
btn.Click.Add
(fun _ -> this.FontSize <- (btn.Tag :?> double))
grid.Children.Add(btn) |> ignore
Grid.SetRow(btn,0)
Grid.SetColumn(btn,i)
let btn = new Button()
btn.Content <- new TextBlock
(new Run("Set button FontSize to " + Float.to_string(fntsizes.[i])))
btn.Tag <- fntsizes.[i]
btn.HorizontalAlignment <- HorizontalAlignment.Center
btn.VerticalAlignment <- VerticalAlignment.Center
// Implement ButtonFontSizeOnClick
btn.Click.Add
(fun _ -> btn.FontSize <- (btn.Tag :?> double))
grid.Children.Add(btn) |> ignore
Grid.SetRow(btn,1)
Grid.SetColumn(btn,i)
()
end
#if COMPILED
[<STAThread()>]
do
let app = new Application() in
app.Run(new SetFontSizeProperty()) |> ignore
#endif
SetSpaceProperty Example - broken
#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
//
(* From Chap 8 - SetSpaceProperty example with DependencyProperty *)
//
(* From Chap 8 - SpaceButton.cs *)
type SpaceButton = class
inherit Button as base
val mutable txt: string
new () as this = {txt = null}
static member initSpaceProperty =
let metadata = new FrameworkPropertyMetadata()
metadata.DefaultValue <- 1
metadata.AffectsMeasure <- true
metadata.Inherits <- true
metadata.PropertyChangedCallback <- new PropertyChangedCallback(SpaceButton.OnSpacePropertyChanged)
DependencyProperty.Register("Space",
typeof<int>,
typeof<SpaceButton>,
metadata,
// callback method for value validation
(fun obj -> let i = (obj :?> int) in (i >= 0)))
// I'm completely stumped...I don't know how to setup a static readonly property
// in F# and I don't know how to initialize SpaceProperty outside of SpaceButton
// class due to initialization dependency with SpaceButton.
// The closest thing I can find on the web is the following blog by Lewis Bruck
// http://blogs.msdn.com/lbruck/archive/2006/05/24/606653.aspx which also indicates
// that F# and SQL2005 CLR has problems because F# does not generate static readonly
// fields. Don Syme apparently has thought about this subject as he has published an
// article "An Alternative Approach to Initializing Mutually Referential Objects"
// to be found http://research.microsoft.com/~dsyme/papers/valrec-tr.pdf
static member SpaceProperty = SpaceButton.initSpaceProperty
static member OnSpacePropertyChanged (obj:DependencyObject) (args:DependencyPropertyChangedEventArgs) =
let btn = obj :?> SpaceButton
btn.Content <- btn.SpaceOutText btn.txt
member this.Text
with get() = this.txt
and set value =
this.txt <- value
this.Content <- this.SpaceOutText(this.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
end
(* From Chap 8 - SpaceWindow.cs *)
type SpaceWindow = class
inherit Window as base
new () as this = {}
// A static DependencyProperty
static member SpaceProperty =
let metadata = new FrameworkPropertyMetadata()
metadata.Inherits <- true
// Add owner to SpaceProeprty & override metadata
let prop = SpaceButton.SpaceProperty.AddOwner(typeof<SpaceWindow>)
prop.OverrideMetadata(typeof<SpaceWindow>,metadata)
prop
member this.Space
with get() = (this.GetValue(SpaceWindow.SpaceProperty) :?> int)
and set (value:int) = this.SetValue(SpaceWindow.SpaceProperty,value)
end
(* From Chap 8 - SetSpaceProperty.cs *)
type SetSpaceProperty = class
inherit SpaceWindow as base
new () as this = {} then
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();
btn.Text <- "Set window Space to " + Int32.to_string(iSpaces.[i])
btn.Tag <- iSpaces.[i];
btn.HorizontalAlignment <- HorizontalAlignment.Center
btn.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()
btn.Text <- "Set button Space to " + Int32.to_string(iSpaces.[i])
btn.Tag <- iSpaces.[i];
btn.HorizontalAlignment <- HorizontalAlignment.Center
btn.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)
end
#if COMPILED
[<STAThread()>]
do
let app = new Application() in
app.Run(new SetSpaceProperty()) |> ignore
#endif
2 comments:
Hi John!
This is a known issue, which crops up particularly in GUI code, but there are nearly always workarounds. For example, how about using a
let mutable private initSpaceProperty : DependencyProperty = null
prior to the first class, then initializing on demand? Full code below (I switched to using implicit construction for the classes)
#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
//
(* From Chap 8 - SetSpaceProperty example with DependencyProperty *)
//
(* From Chap 8 - SpaceButton.cs *)
let mutable private initSpaceProperty : DependencyProperty = null
type SpaceButton() =
inherit Button() as base
let mutable txt = null
static member SpaceProperty =
if initSpaceProperty = null then
let metadata = new FrameworkPropertyMetadata(DefaultValue=1, AffectsMeasure=true, Inherits=true)
metadata.PropertyChangedCallback <- new PropertyChangedCallback(SpaceButton.OnSpacePropertyChanged)
initSpaceProperty <-
DependencyProperty.Register("Space",
typeof<int>,
typeof<SpaceButton>,
metadata,
// callback method for value validation
(fun obj -> let i = (obj :?> int) in (i >= 0)))
initSpaceProperty
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 *)
type SpaceWindow() =
inherit Window() as base
// A static DependencyProperty
static member SpaceProperty =
let metadata = new FrameworkPropertyMetadata()
metadata.Inherits <- true
// Add owner to SpaceProeprty & override metadata
let prop = SpaceButton.SpaceProperty.AddOwner(typeof<SpaceWindow>)
prop.OverrideMetadata(typeof<SpaceWindow>,metadata)
prop
member this.Space
with get() = (this.GetValue(SpaceWindow.SpaceProperty) :?> int)
and set (value:int) = this.SetValue(SpaceWindow.SpaceProperty,value)
(* From Chap 8 - SetSpaceProperty.cs *)
type SetSpaceProperty() as this =
inherit SpaceWindow() as base
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
In case you are interested in making money from your visitors via popunder advertisments, you should run with one of the biggest networks - Propeller Ads.
Post a Comment