F# has a couple of basic generic collections including Array, List and Sequences. It was never clear in my mind when I should use what. My initial views are that I should use Array for mutable list, List for non-lazy and mutation free (aka immutable) list and Sequences for lazy, mutation-free lists. But these are conceptual understanding which is quite different from practical usage applications. I have almost no experience with lazy lists and bulk of my past programming experiences are with mutable lists. The only immutable list that I've dealt with on a consistent basis is String object and that's a pathological case in the sense that most of the time, I don't think of Strings as immutable lists.
So it was that imprecise view of lists that I tried to tackle Chapter 17 of Petzold's book Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation and ran into a wall while working with PrintWithMargins
example. I built the margin TextBoxes as a sequence and tried to update the Text field of those TextBoxes and it would not work. Suddenly, it dawned on me that I should not be using Seq.find
and instead should be using Array.find
. It appears that Seq.find
returns a copy of the TextBox while it is Array.find
that returns the reference to the original TextBox. I suppose it's useful to maintain a distinction between these different collections as immutable lists are more amenable to be offloaded to different processors for processing while mutable lists must run on a single cpu.
Here are the examples from Chapter 17:
PrintOnClick
#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 stack = new StackPanel()
let btn = new Button(Content = "_Print...",
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(24.0))
// PrintOnClick impelmentation
btn.Click.Add(fun _ ->
let dlg = new PrintDialog()
let vis = new DrawingVisual()
using (vis.RenderOpen()) (fun dc ->
(Brushes.LightGray,
new Pen(Brushes.Black, 3.0),
new Point(dlg.PrintableAreaWidth /2.0,
dlg.PrintableAreaHeight / 2.0),
dlg.PrintableAreaWidth / 2.0,
dlg.PrintableAreaHeight / 2.0) |> dc.DrawEllipse
)
(vis, "My first print job") |> dlg.PrintVisual
)
btn |> stack.Children.Add |> ignore
let window = new Window(Title="Print Ellipse",
FontSize=24.0,
Content=stack)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endifPrintWithMargins
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"ReachFramework.dll"
#r @"System.Printing.dll"
open System
open System.Globalization
open System.Windows
open System.Windows.Controls
open System.Windows.Controls.Primitives
open System.Windows.Input
open System.Windows.Media
open System.Printing
//
// From Chapter 17 - PrintWithMargins
//
// PageMarginsDialog
type PageMarginsDialog() = class
inherit Window() as base
let sides = seq ["Left";"Right";"Top";"Bottom"]
let createTextBox label = new TextBox(Name=label,
MinWidth=48.0,
Text="1.40",
Margin = new Thickness(6.0))
let createMarginTextBoxes list = list |> Seq.map createTextBox
// I originally built this findTextBox with the following statement:
//
// let findTextBox (textboxes:TextBox array) side =
// textboxes |> Seq.find (fun textbox -> textbox.Name = side)
//
// and kept wondering why my PageMargins set function doesn't work.
// After fruitless flailing about with the debugger, it finally dawned on me
// that Seq.find returns a copy while Array.find returns the reference to
// the original TextBox, which is what I wanted.
let findTextBox (textboxes:TextBox array) side =
textboxes |> Array.find (fun textbox -> textbox.Name = side)
let mutable margins=null
do
margins <- Array.of_seq (createMarginTextBoxes sides)
base.Title <- "Page Setup"
base.ShowInTaskbar <- false
base.WindowStyle <- WindowStyle.ToolWindow
base.WindowStartupLocation <- WindowStartupLocation.CenterOwner
base.SizeToContent <- SizeToContent.WidthAndHeight
base.ResizeMode <- ResizeMode.NoResize
let stack = new StackPanel()
base.Content <- stack
let grid = new Grid(Margin = new Thickness(6.0))
let grpbox = new GroupBox(Header = "Margins (inches)",
Content = grid,
Margin = new Thickness(12.0))
grpbox |> stack.Children.Add |> ignore
[1..3] |> Seq.iter (fun _ ->
new RowDefinition(Height = GridLength.Auto)
|> grid.RowDefinitions.Add)
[1..4] |> Seq.iter (fun _ ->
new ColumnDefinition (Width = GridLength.Auto)
|> grid.ColumnDefinitions.Add)
// Use UniformGrid fo OK and Cancel Buttons
let unigrid = new UniformGrid(Rows = 1,Columns = 2)
unigrid |> stack.Children.Add |> ignore
let ok = new Button(Content="OK",
IsDefault = true,
IsEnabled = false,
MinWidth = 60.0,
Margin = new Thickness(12.0),
HorizontalAlignment = HorizontalAlignment.Center)
let dialog = base
ok.Click.Add(fun _ -> dialog.DialogResult <- new Nullable<Boolean>(true))
ok |> unigrid.Children.Add |> ignore
let cancel = new Button(Content="Cancel",
IsCancel = true,
IsEnabled = true,
MinWidth = 60.0,
Margin = new Thickness(12.0),
HorizontalAlignment = HorizontalAlignment.Center)
cancel |> unigrid.Children.Add |> ignore
sides |> Seq.iteri (fun i side ->
let lbl = new Label(Content="_" + side + ":",
Margin=new Thickness(6.0),
VerticalAlignment = VerticalAlignment.Center)
grid.Children.Add(lbl) |> ignore
Grid.SetRow(lbl,i/2)
Grid.SetColumn(lbl, 2*(i%2))
let txtbox = findTextBox margins side
txtbox.TextChanged.Add(fun _ ->
ok.IsEnabled <- margins
|> Seq.map (fun txtbox -> txtbox.Text)
|> Seq.map (fun x-> Double.TryParse(x) |> fst)
|> Seq.fold1 (fun x y -> x && y))
grid.Children.Add(txtbox) |> ignore
Grid.SetRow(txtbox,i/2)
Grid.SetColumn(txtbox,2*(i%2)+1)
)
member this.PageMargins
with get() =
Console.WriteLine("Getting Page Margins:")
margins |> Seq.iter (fun x -> Console.WriteLine(x.Text))
let findTextBoxSide = findTextBox margins
new Thickness(Double.Parse((findTextBoxSide "Left").Text)*96.0,
Double.Parse((findTextBoxSide "Right").Text)*96.0,
Double.Parse((findTextBoxSide "Top").Text)*96.0,
Double.Parse((findTextBoxSide "Bottom").Text)*96.0)
and set (value:Thickness) =
Console.WriteLine("Setting Page Margins:")
margins |> Seq.iter (fun x -> Console.WriteLine("{0} : {1}",x.Name,x.Text))
let findTextBoxSide = findTextBox margins
(findTextBoxSide "Left").Text <- (value.Left /96.0).ToString("F3")
(findTextBoxSide "Right").Text <- (value.Right /96.0).ToString("F3")
(findTextBoxSide "Top").Text <- (value.Top /96.0).ToString("F3")
(findTextBoxSide "Bottom").Text <- (value.Bottom /96.0).ToString("F3")
margins |> Seq.iter (fun x -> Console.WriteLine(x.Text))
end
let mutable prnqueue:PrintQueue = null
let mutable prntkt:PrintTicket = null
let mutable marginPage = new Thickness(96.0)
let stack = new StackPanel()
let window = new Window(Title="Print with Margins",
FontSize=24.0,
Content=stack)
let pagesetupBtn = new Button(Content = "Page Set_up...",
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(24.0))
pagesetupBtn |> stack.Children.Add |> ignore
let printBtn = new Button(Content = "_Print",
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(24.0))
printBtn |> stack.Children.Add |> ignore
pagesetupBtn.Click.Add(fun _ ->
let dlg = new PageMarginsDialog()
dlg.Owner <- window
dlg.PageMargins <- marginPage
let result = dlg.ShowDialog().GetValueOrDefault()
if result then
marginPage <- dlg.PageMargins
)
printBtn.Click.Add(fun _ ->
let dlg = new PrintDialog()
if prnqueue <> null then
dlg.PrintQueue <- prnqueue
if prntkt <> null then
dlg.PrintTicket <- prntkt
let vis = new DrawingVisual()
using (vis.RenderOpen()) (fun dc ->
let pen = new Pen(Brushes.Black,1.0)
let marginWidth = (marginPage.Left + marginPage.Top)
let marginHeight = (marginPage.Top + marginPage.Bottom)
let rectPage = new Rect(marginPage.Left,
marginPage.Top,
dlg.PrintableAreaWidth - marginWidth,
dlg.PrintableAreaHeight - marginHeight)
dc.DrawRectangle(null,pen,rectPage)
let formtxt = new FormattedText(String.Format("Hello, Printer {0} x {1}",
dlg.PrintableAreaWidth/96.0,
dlg.PrintableAreaHeight/96.0),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(new FontFamily("Times New Roman"),
FontStyles.Italic,
FontWeights.Normal,
FontStretches.Normal),
48.0,
Brushes.Black)
let ptText = new Point(rectPage.Left + (rectPage.Width - formtxt.Width) / 2.0,
rectPage.Top + (rectPage.Height - formtxt.Height) / 2.0)
let sizeText = new Size(formtxt.Width,formtxt.Height)
dc.DrawText(formtxt,ptText)
dc.DrawRectangle(null,pen,new Rect(ptText,sizeText))
)
dlg.PrintVisual(vis,window.Title)
()
)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endifPrintaBunchaButtons
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"ReachFramework.dll"
#r @"System.Printing.dll"
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 17 - PrintaBunchaButtons
//
let btn = new Button(FontSize = 24.0,
Content = "Print ...",
Padding = new Thickness(12.0),
Margin = new Thickness(96.0))
let window = new Window(Title="Print a Bunch of Buttons",
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.CanMinimize,
Content=btn)
// PrintOnClick implementation
btn.Click.Add(fun _ ->
let dlg = new PrintDialog()
let retval = dlg.ShowDialog().GetValueOrDefault()
if retval then
let grid = new Grid(Background=new LinearGradientBrush(Colors.Gray,
Colors.White,
new Point(0.0, 0.0),
new Point(1.0, 1.0)))
[1..5] |> Seq.iter (fun _ ->
new ColumnDefinition(Width = GridLength.Auto)
|> grid.ColumnDefinitions.Add
new RowDefinition(Height = GridLength.Auto)
|> grid.RowDefinitions.Add)
let rand = new Random()
[0..24] |> Seq.iteri (fun i _ ->
let btn = new Button(FontSize=12.0 + (Int32.to_float (rand.Next(8))),
Content = "Button No. " + (Int32.to_string (i+1)),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(6.0))
btn |> grid.Children.Add |> ignore
Grid.SetRow(btn, i%5)
Grid.SetColumn(btn,i/5))
// Size the grid
grid.Measure(new Size(Double.PositiveInfinity,
Double.PositiveInfinity))
let sizeGrid = grid.DesiredSize
// Determine point for centering Grid on page
let ptGrid = new Point((dlg.PrintableAreaWidth - sizeGrid.Width) / 2.0,
(dlg.PrintableAreaHeight - sizeGrid.Height) / 2.0)
// Layout pass
grid.Arrange(new Rect(ptGrid,sizeGrid))
// print it
dlg.PrintVisual(grid,window.Title))
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
PrintBanner
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"ReachFramework.dll"
#r @"System.Printing.dll"
open System
open System.Globalization
open System.Printing
open System.Windows
open System.Windows.Controls
open System.Windows.Documents
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 17 - PrintBanner
//
type BannerDocumentPaginator() = class
inherit DocumentPaginator() as base
let mutable txt = ""
let mutable face = new Typeface("")
let mutable sizeMax = new Size(0.0,0.0)
let mutable (sizePage:Size) = new Size(0.0,0.0)
let getFormattedText (ch:char) (face:Typeface) (em:double) =
new FormattedText(ch.ToString(),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
face,
em,
Brushes.Black)
member this.Text
with get() = txt
and set value = txt <-value
member this.Typeface
with get() = face
and set value = face <-value
override this.IsPageCountValid
with get() =
txt.ToCharArray() |> Seq.iter (fun ch ->
let formtxt = getFormattedText ch face 100.0
sizeMax.Width <- Math.Max(sizeMax.Width,formtxt.Width)
sizeMax.Height <- Math.Max(sizeMax.Height ,formtxt.Height )
)
true
override this.PageCount
with get() = if txt = null then 0 else txt.Length
override this.PageSize
with get() = sizePage
and set value = sizePage <- value
override this.Source
with get() = null
override this.GetPage (numPage) =
let vis = new DrawingVisual()
using (vis.RenderOpen()) (fun dc ->
let factor = Math.Min((this.PageSize.Width - 96.0) / sizeMax.Width,
(this.PageSize.Height - 96.0) / sizeMax.Height)
let formtxt = getFormattedText (txt.Chars numPage) face (factor*100.0)
let ptText = new Point((this.PageSize.Width - formtxt.Width) / 2.0,
(this.PageSize.Height - formtxt.Height) / 2.0)
dc.DrawText(formtxt,ptText))
//
new DocumentPage(vis)
end
// PrintBanner
let stack = new StackPanel()
let window = new Window(Title="Print Banner",
SizeToContent = SizeToContent.WidthAndHeight,
Content=stack)
let txtbox = new TextBox(Width=250.0,
Margin= new Thickness(12.0))
txtbox |> stack.Children.Add |> ignore
let btn = new Button(Content = "_Print...",
Margin = new Thickness(12.0),
HorizontalAlignment = HorizontalAlignment.Center)
btn |> stack.Children.Add |> ignore
btn.Click.Add(fun _ ->
let dlg = new PrintDialog()
let retval = dlg.ShowDialog().GetValueOrDefault()
if retval then
let prntkt = dlg.PrintTicket
prntkt.PageOrientation <- new Nullable<PageOrientation>(PageOrientation.Portrait)
// Create new BannerDocumentPaginator object.
let paginator = new BannerDocumentPaginator(Text=txtbox.Text)
paginator.PageSize <- new Size(dlg.PrintableAreaWidth,
dlg.PrintableAreaHeight)
dlg.PrintDocument(paginator, "Banner: " + txtbox.Text))
txtbox.Focus()
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
ChooseFont
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"ReachFramework.dll"
#r @"System.Printing.dll"
open System
open System.Collections
open System.Printing
open System.Windows
open System.Windows.Controls
open System.Windows.Documents
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 17 - ChooseFont
//
let minus1 x = x-1
let plus1 x = x+1
type Lister() = class
inherit ContentControl() as base
let list = new ArrayList()
let stack = new StackPanel()
let mutable indexSelected = -1
let scroll = new ScrollViewer()
let triggerSelectionChanged,selectionChangedEvent = IEvent.create()
let ScrollIntoView() =
if (list.Count > 0 &&
indexSelected <> -1 &&
scroll.ViewportHeight <= scroll.ExtentHeight) then
let heightPerItem = scroll.ExtentHeight / float list.Count
let offsetItemTop = float indexSelected * heightPerItem
let offsetItemBot = float (indexSelected+1) * heightPerItem
if offsetItemTop < scroll.VerticalOffset then
offsetItemTop |> scroll.ScrollToVerticalOffset
else if offsetItemBot > (scroll.VerticalOffset + scroll.ViewportHeight) then
(scroll.VerticalOffset +
offsetItemBot -
scroll.VerticalOffset -
scroll.ViewportHeight) |> scroll.ScrollToVerticalOffset
()
let highlightSelected () =
stack.Children
|> IEnumerable.untyped_to_typed
|> IEnumerable.iter (fun (txtblock:TextBlock) ->
txtblock.Background <- SystemColors.WindowBrush
txtblock.Foreground <- SystemColors.WindowTextBrush)
if indexSelected > -1 then
let txtblock = stack.Children.[indexSelected] :?> TextBlock
txtblock.Background <- SystemColors.HighlightBrush
txtblock.Foreground <- SystemColors.HighlightTextBrush
ScrollIntoView()
do
base.Focusable <- false
// Make border the content of ContentControl
let bord = new Border(BorderThickness = new Thickness(1.0),
BorderBrush = SystemColors.ActiveBorderBrush,
Background = SystemColors.WindowBrush)
scroll.Focusable <- false
scroll.Content <- stack
scroll.Padding <- new Thickness(2.0, 0.0, 0.0, 0.0)
bord.Child <- scroll
base.Content <- bord
let baseCtl = base
// Implement ScrollIntoView
baseCtl.Loaded.Add(fun _ -> ScrollIntoView())
// Handle mouse left button down event
// Define the handler
let LMouseClick _ (args:MouseButtonEventArgs) =
match args.Source with
| :? TextBlock as text ->
indexSelected <- stack.Children.IndexOf(text)
highlightSelected ()
triggerSelectionChanged()
| _ -> ()
// Adding new handler
base.AddHandler(TextBlock.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(LMouseClick))
member this.SelectionChanged = selectionChangedEvent
member this.SelectedIndex
with get() =
indexSelected
and set value =
if ((value < -1) || (value >= this.Count)) then
raise ( ArgumentOutOfRangeException("SelectedIndex"))
indexSelected <- value
highlightSelected ()
// Trigger Selection Changed Event
triggerSelectionChanged()
member this.Add (o:obj) =
list.Add(o) |> ignore
let textblock = new TextBlock(Text=o.ToString())
stack.Children.Add(textblock) |> ignore
member this.Insert index (o:obj) =
list.Insert(index, o) |> ignore
(index,new TextBlock(Text=o.ToString()))
|> stack.Children.Insert |> ignore
member this.Clear() =
this.SelectedIndex <- -1
stack.Children.Clear()
list.Clear()
member this.Contains o =
list.Contains(o)
member this.Count
with get() = list.Count
member this.GoToLetter c =
// need to re-implement
let charlist = Array.map (fun (o:obj) -> o.ToString()) (list.ToArray())
try
let index = Array.find_index (fun item ->
try
(String.index (String.uppercase item) (Char.ToUpper(c))) = 0
with _ -> false) charlist
if index = this.SelectedIndex then
this.SelectedIndex <- index +1
else
this.SelectedIndex <- index
with _ -> this.SelectedIndex <- -1
member this.SelectedItem
with get() =
if this.SelectedIndex > -1 then
list.[this.SelectedIndex]
else
null
and set (value:obj) = this.SelectedIndex <- list.IndexOf(value)
member this.PageUp () =
if (this.SelectedIndex = -1 || this.Count = 0) then ()
else
let index = this.SelectedIndex -
Float.to_int ((Float.of_int this.Count) * scroll.ViewportHeight/scroll.ExtentHeight)
this.SelectedIndex <- Math.Max(index,0)
member this.PageDown () =
if (this.SelectedIndex = -1 || this.Count = 0) then ()
else
let index = this.SelectedIndex +
Float.to_int ((Float.of_int this.Count) * scroll.ViewportHeight/scroll.ExtentHeight)
this.SelectedIndex <- Math.Min(index,this.Count-1)
end
// TextBoxWithLister
type TextBoxWithLister() = class
inherit ContentControl() as base
let txtbox = new TextBox()
let lister = new Lister()
let mutable isReadOnly = true
let triggerSelectionChanged,selectionChangedEvent = IEvent.create()
let triggerTextChanged,textChangedEvent = IEvent.create()
do
let dock = new DockPanel()
base.Content <- dock
dock.Children.Add(txtbox) |> ignore
DockPanel.SetDock(txtbox,Dock.Top)
txtbox.TextChanged.Add(fun _ -> triggerTextChanged())
// ListerOnSelectionChanged
lister.SelectionChanged.Add(fun _ ->
if lister.SelectedIndex = -1 then
txtbox.Text <- ""
else txtbox.Text <- lister.SelectedItem.ToString()
// OnSelectionChanged(args)
triggerSelectionChanged()
)
dock.Children.Add(lister) |> ignore
()
member this.SelectionChanged = selectionChangedEvent
member this.TextChanged = textChangedEvent
member this.Add (o:obj) =
lister.Add(o)
member this.Insert index o =
lister.Insert index o
member this.Contains (o:obj) =
lister.Contains(o)
member this.Clear () =
lister.Clear()
member this.Text
with get() = txtbox.Text
and set value = txtbox.Text <- value
member this.Count
with get() = lister.Count
member this.IsReadOnly
with get() = isReadOnly
and set value = isReadOnly <- value
member this.SelectedIndex
with get() = lister.SelectedIndex
and set value =
lister.SelectedIndex <- value
if lister.SelectedIndex > -1 then
txtbox.Text <- lister.SelectedItem.ToString()
else txtbox.Text <- ""
member this.SelectedItem
with get() = lister.SelectedItem
and set (value:obj) =
lister.SelectedItem <- value
if lister.SelectedItem <> null then
txtbox.Text <- lister.SelectedItem.ToString()
else txtbox.Text <- ""
override this.OnMouseDown (args) =
base.OnMouseDown(args)
this.Focus() |> ignore
override this.OnPreviewTextInput (args) =
base.OnPreviewTextInput(args)
if isReadOnly then
lister.GoToLetter(args.Text.[0])
args.Handled <- true
override this.OnPreviewKeyDown (args) =
base.OnKeyDown(args)
if this.SelectedIndex > -1 then
match args.Key with
| Key.Home -> if lister.Count > 0
then this.SelectedIndex <- 0
| Key.End -> if lister.Count > 0
then this.SelectedIndex <- lister.Count -1
| Key.Down -> if lister.Count > 0
then this.SelectedIndex <- plus1 this.SelectedIndex
| Key.Up -> if this.SelectedIndex < (lister.Count-1)
then this.SelectedIndex <- minus1 this.SelectedIndex
| Key.PageUp -> lister.PageUp()
| Key.PageDown -> lister.PageDown()
| _ -> ()
end
// FontDialog
type FontDialog() = class
inherit Window() as base
let boxFamily = new TextBoxWithLister()
let boxStyle = new TextBoxWithLister()
let boxWeight = new TextBoxWithLister()
let boxStretch = new TextBoxWithLister()
let boxSize = new TextBoxWithLister()
let mutable canUpdate = false
let lblDisplay = new Label()
let setTypeface (box:TextBoxWithLister) (o:obj) =
if box.Contains(o) then box.SelectedItem <- o
else box.SelectedIndex <- 0
do
base.Title <- "Font"
base.ShowInTaskbar <- false
base.WindowStyle <- WindowStyle.ToolWindow
base.WindowStartupLocation <- WindowStartupLocation.CenterOwner
base.SizeToContent <- SizeToContent.WidthAndHeight
base.ResizeMode <- ResizeMode.NoResize
let gridMain = new Grid()
base.Content <- gridMain
[new GridLength(200.0, GridUnitType.Pixel); // TextBoxWithLister controls
new GridLength(150.0, GridUnitType.Pixel); // Sample text
GridLength.Auto] // Buttons
|> Seq.iter (fun height ->
let rowdef = new RowDefinition(Height=height)
gridMain.RowDefinitions.Add(rowdef))
gridMain.ColumnDefinitions.Add
(let coldef = new ColumnDefinition(Width=new GridLength(650.0, GridUnitType.Pixel))
coldef)
let gridBoxes = new Grid()
gridMain.Children.Add(gridBoxes) |> ignore
lblDisplay.Content <- "AaBbCc XxYzZz 012345"
lblDisplay.HorizontalContentAlignment <- HorizontalAlignment.Center
lblDisplay.VerticalContentAlignment <- VerticalAlignment.Center
gridMain.Children.Add(lblDisplay) |> ignore
Grid.SetRow(lblDisplay,1)
// Create 2 rows
[GridLength.Auto;
new GridLength(100.0, GridUnitType.Star)]
|> Seq.iter (fun height ->
let rowdef = new RowDefinition(Height=height)
gridBoxes.RowDefinitions.Add(rowdef))
// Create 5 columns
[175.0;100.0;100.0;100.0;75.0]
|> Seq.iter (fun size ->
let coldef = new ColumnDefinition(Width=new GridLength(size,GridUnitType.Star))
gridBoxes.ColumnDefinitions.Add(coldef))
[("Font Family",boxFamily,true);
("Style",boxStyle,true);
("Weight",boxWeight,true);
("Stretch",boxStretch,true);
("Size",boxSize,false); ]
|> Seq.iteri (fun i (label,txtboxlister,readOnlyFlag) ->
let lbl = new Label(Content=label,
Margin= new Thickness(12.0,12.0,12.0,0.0))
gridBoxes.Children.Add(lbl) |> ignore
Grid.SetRow(lbl,0)
Grid.SetColumn(lbl,i)
txtboxlister.IsReadOnly <- readOnlyFlag
txtboxlister.Margin <- new Thickness(12.0,0.0,12.0,12.0)
gridBoxes.Children.Add(txtboxlister) |> ignore
Grid.SetRow(txtboxlister,1)
Grid.SetColumn(txtboxlister,i))
// Create five-column Grid for Buttons
let gridButtons = new Grid()
gridMain.Children.Add(gridButtons) |>ignore
Grid.SetRow(gridButtons,2)
[1..5] |> Seq.iter ( fun _ ->
gridButtons.ColumnDefinitions.Add(new ColumnDefinition()))
let ok = new Button(Content = "OK",
IsDefault = true,
HorizontalAlignment = HorizontalAlignment.Center,
MinWidth = 60.0,
Margin = new Thickness(12.0))
let baseCtl = base
ok.Click.Add(fun _ -> basectl.DialogResult <- new Nullable<Boolean>(true))
gridButtons.Children.Add(ok) |> ignore
Grid.SetColumn(ok,1)
let cancel = new Button(Content = "Cancel",
IsCancel = true,
HorizontalAlignment = HorizontalAlignment.Center,
MinWidth = 60.0,
Margin = new Thickness(12.0))
gridButtons.Children.Add(cancel) |> ignore
Grid.SetColumn(cancel,3)
// Add content to FontFamily box
Fonts.SystemFontFamilies
|> Seq.iter (fun fam ->
boxFamily.Add(fam))
// Initialize font sizes
[8;9;10;11;12;14;16;18;20;22;24;26;28;36;48;72]
|> Seq.iter (fun size -> boxSize.Add(Int32.to_string size))
// Set selection changed events
boxFamily.SelectionChanged.Add( fun _ ->
canUpdate <- false
let family = boxFamily.SelectedItem :?> FontFamily
let checkfonts = boxFamily
// Get previous font values
let prevStyle =
if (boxStyle.Count > 0 && boxStyle.SelectedItem <> null) then
boxStyle.SelectedItem :?> FontStyle
else
FontStyles.Normal
let prevWeight =
if (boxWeight.Count > 0 && boxWeight.SelectedItem <> null) then
boxWeight.SelectedItem :?> FontWeight
else
FontWeights.Normal
let prevStretch =
if (boxStretch.Count > 0 && boxStretch.SelectedItem <> null) then
boxStretch.SelectedItem :?> FontStretch
else
FontStretches.Normal
boxStyle.Clear()
boxWeight.Clear()
boxStretch.Clear()
let setNormalTop (container:TextBoxWithLister) value defaultValue =
if container.Contains(value) then ()
else
if value = defaultValue then
container.Insert 0 (box(value))
else
container.Add value
family.FamilyTypefaces |> Seq.iter (fun ftf ->
// Set Style
setNormalTop boxStyle ftf.Style FontStyles.Normal
// Set Weight
setNormalTop boxWeight ftf.Weight FontWeights.Normal
// Set Stretch
setNormalTop boxStretch ftf.Stretch FontStretches.Normal)
// Need to rewrite this!
let setSelected (container:TextBoxWithLister) value =
if (container.Contains(value)) then
container.SelectedItem <- value
else
container.SelectedIndex <- 0
let style = boxStyle
let weight = boxWeight
let stretch = boxStretch
setSelected boxStyle prevStyle
setSelected boxWeight prevWeight
setSelected boxStretch prevStretch
lblDisplay.FontFamily <- family
lblDisplay.FontStyle <- (boxStyle.SelectedItem :?> FontStyle)
lblDisplay.FontWeight <- (boxWeight.SelectedItem :?> FontWeight)
lblDisplay.FontStretch <- (boxStretch.SelectedItem :?> FontStretch)
canUpdate <- true
)
boxWeight.SelectionChanged.Add( fun _ ->
if canUpdate then
lblDisplay.FontWeight <- (boxWeight.SelectedItem :?> FontWeight))
boxStyle.SelectionChanged.Add( fun _ ->
if canUpdate then
lblDisplay.FontStyle <- (boxStyle.SelectedItem :?> FontStyle))
boxStretch.SelectionChanged.Add( fun _ ->
if canUpdate then
lblDisplay.FontStretch <- (boxStretch.SelectedItem :?> FontStretch))
boxSize.SelectionChanged.Add( fun _ ->
let (isDouble,size) = Double.TryParse(boxSize.Text)
if isDouble then lblDisplay.FontSize <- (size/0.75))
boxFamily.Focus() |> ignore
member this.Typeface
with get() =
new Typeface((boxFamily.SelectedItem :?> FontFamily),
(boxStyle.SelectedItem :?> FontStyle),
(boxWeight.SelectedItem :?> FontWeight),
(boxStretch.SelectedItem :?> FontStretch))
and set (value:Typeface) =
setTypeface boxFamily (box(value.FontFamily))
setTypeface boxStyle (box(value.Style))
setTypeface boxWeight (box(value.Weight))
setTypeface boxStretch (box(value.Stretch))
member this.FaceSize
with get() =
let (isDouble,size) = Double.TryParse(boxSize.Text)
if isDouble then (size/0.75)
else 8.25
and set (value:double) =
let size = 0.75*value
boxSize.Text <- size.ToString()
if boxSize.Contains(size) then boxSize.SelectedItem <- size
else boxSize.Insert 0 (box(size.ToString()))
end
// PrintBanner
let stack = new StackPanel()
let window = new Window(Title="Choose Font",
Top=200.0,
Left=300.0,
SizeToContent = SizeToContent.WidthAndHeight)
let btn = new Button(Content=window.Title,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center)
window.Content <- btn
btn.Click.Add(fun _ ->
let dlg = new FontDialog()
dlg.Owner <- window
dlg.Typeface <- new Typeface(window.FontFamily,
window.FontStyle,
window.FontWeight,
window.FontStretch)
dlg.FaceSize <- window.FontSize
let retval = dlg.ShowDialog().GetValueOrDefault()
if retval then
window.FontFamily <- dlg.Typeface.FontFamily
window.FontStyle <- dlg.Typeface.Style
window.FontWeight <- dlg.Typeface.Weight
window.FontStretch <- dlg.Typeface.Stretch
window.FontSize <- dlg.FaceSize)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
PrintBetterBanner
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"ReachFramework.dll"
#r @"System.Printing.dll"
#r "printlib.dll"
open System
open System.Collections
open System.Printing
open System.Windows
open System.Windows.Controls
open System.Windows.Documents
open System.Windows.Input
open System.Windows.Media
open Chapter17
//
// From Chapter 17 - PrintBetterBanner
//
let stack = new StackPanel()
let window = new Window(Title="Print Better Banner",
Content=stack,
SizeToContent = SizeToContent.WidthAndHeight)
let mutable face = new Typeface(window.FontFamily,
window.FontStyle,
window.FontWeight,
window.FontStretch)
let txtbox = new TextBox(Width=250.0,
Margin=new Thickness(12.0))
stack.Children.Add(txtbox)
let fontclick _ =
let dlg = new FontDialog()
dlg.Owner <- window
dlg.Typeface <- face
if (dlg.ShowDialog().GetValueOrDefault()) then
face <- dlg.Typeface
let printclick _ =
let dlg = new PrintDialog()
if (dlg.ShowDialog().GetValueOrDefault()) then
let prntkt = dlg.PrintTicket
prntkt.PageOrientation <- new Nullable<PageOrientation>(PageOrientation.Portrait)
dlg.PrintTicket <- prntkt
let paginator = new BannerDocumentPaginator()
paginator.Text <- txtbox.Text
paginator.Typeface <- face
paginator.PageSize <- new Size(dlg.PrintableAreaWidth,
dlg.PrintableAreaWidth)
dlg.PrintDocument(paginator,"Banner: " + txtbox.Text)
// Add Font and Print Buttons
[("_Font...",fontclick);
("_Print...",printclick)]
|> Seq.iter (fun (label,func) ->
let btn = new Button(Content=label,
Margin=new Thickness(12.0),
HorizontalAlignment = HorizontalAlignment.Center)
stack.Children.Add(btn) |> ignore
btn.Click.Add(func))
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif