Working through Chapter 16 of Petzold's bookApplications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation.
The most interesting effort in working through these examples is converting the repeated if
statements in the implementation of Convert
method in MetadataToFlags
to use zip,filter,map and combine
. The column definitions in DependencyPropertyListView
has also been refactored to define all the definitions in a list and iterate through the list to create each GridViewColumn
.
ManuallyPopulateTreeView
#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 Chapter 16 - ManuallyPopulateTreeView
//
//----------------------------------------------------------
let addSubItems items (branch:TreeViewItem) = items |> Seq.iter (fun item ->
item |> branch.Items.Add |> ignore)
let tree = new TreeView()
tree.Items.Add
(let animalBranch = new TreeViewItem(Header="Animal")
animalBranch.Items.Add
(let branch = new TreeViewItem(Header="Dog")
let dogs = ["Poodle";"Irish Setter";"German Shepherd"]
addSubItems dogs branch
branch) |>ignore
animalBranch.Items.Add
(let branch = new TreeViewItem(Header="Cat")
branch.Items.Add(new TreeViewItem(Header="Alley Cat")) |>ignore
branch.Items.Add(new Button(Content="Noodles")) |>ignore
branch.Items.Add("Siamese") |>ignore
branch) |> ignore
animalBranch.Items.Add
(let branch = new TreeViewItem(Header="Primate")
let primates = ["Chimpanzee";"Bonobo";"Human"]
addSubItems primates branch
branch) |> ignore
animalBranch) |> ignore
tree.Items.Add
(let branch = new TreeViewItem(Header="Mineral")
let minerals = ["Calcium";"Zinc";"Iron"]
addSubItems minerals branch
branch) |> ignore
tree.Items.Add
(let branch = new TreeViewItem(Header="Vegetable")
let vegetables = ["Carrot";"Asparagus";"Broccoli"]
addSubItems vegetables branch
branch) |> ignore
let window = new Window(Title="Manually Populate TreeView",
Content=tree)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
RecurseDirectoriesInefficiently
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
open System
open System.IO
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 16 - RecurseDirectoriesInefficiently
//
//----------------------------------------------------------
let rec GetSubDir (item:TreeViewItem) =
let dir = item.Tag :?> DirectoryInfo
try
dir.GetDirectories() |> Seq.iter (fun subdir ->
let subitem = new TreeViewItem(Header=subdir.Name,Tag=subdir)
subitem |> item.Items.Add |> ignore
// recursively obtain subdirectories
subitem |> GetSubDir)
with _ -> ()
let tree = new TreeView()
tree.Items.Add
(let item = new TreeViewItem(Header = Path.GetPathRoot(Environment.SystemDirectory))
item.Tag <- new DirectoryInfo(item.Header :?> string)
GetSubDir item
item)|>ignore
let window = new Window(Title="Recurse Directories Inefficiently",
Content=tree)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
RecurseDirectoriesIncrementally
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
open System
open System.IO
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Media.Imaging
//----------------------------------------------------------
// From Chapter 16 – RecurseDirectoriesIncrementally
//----------------------------------------------------------
let selectedImage = new BitmapImage(new Uri("file:///icons/OPENFOLD.BMP"))
let unselectedImage = new BitmapImage(new Uri("file:///icons/CLSDFOLD.BMP"))
let cddriveImage = new BitmapImage(new Uri("file:///icons/CDDRIVE.BMP"))
let driveImage = new BitmapImage(new Uri("file:///icons/DRIVE.BMP"))
let floppyImage = new BitmapImage(new Uri("file:///icons/35FLOPPY.BMP"))
//
// From Chapter 16 – ImagedTreeViewItem
//
type ImagedTreeViewItem() = class
inherit TreeViewItem() as base
let stack = new StackPanel(Orientation=Orientation.Horizontal)
let img = new Image(VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 2.0, 0.0))
let text = new TextBlock(VerticalAlignment = VerticalAlignment.Center)
let mutable srcSelected:ImageSource = null
let mutable srcUnselected:ImageSource = null
do
base.Header <- stack
img |> stack.Children.Add |> ignore
text |> stack.Children.Add |> ignore
member this.Text
with get() = text.Text
and set value =
text.Text <- value
member this.SelectedImage
with get() = srcSelected
and set value =
srcSelected <- value
if (this.IsSelected) then img.Source <- srcSelected
member this.UnselectedImage
with get() = srcUnselected
and set value =
srcUnselected <- value
if (this.IsSelected=false) then img.Source <- srcUnselected
override this.OnSelected (args:RoutedEventArgs ) =
base.OnSelected(args)
img.Source <- selectedImage
override this.OnUnselected (args:RoutedEventArgs ) =
base.OnUnselected(args)
img.Source <- unselectedImage
end
//
// From Chapter 16 – DirectoryTreeViewItem
//
type DirectoryTreeViewItem = class
inherit ImagedTreeViewItem as base
val mutable dir:DirectoryInfo
new (rootdir:DirectoryInfo) as this = {dir=rootdir} then
base.Text <- this.dir.Name
member this.DirectoryInfo
with get() = this.dir
member this.Populate () =
try
this.dir.GetDirectories()
|> Seq.iter (fun child ->
new DirectoryTreeViewItem(child)
|> this.Items.Add |> ignore)
with _ -> ()
override this.OnExpanded (args:RoutedEventArgs) =
base.OnExpanded(args)
this.Items |> Seq.untyped_to_typed
|> Seq.iter (fun (item:DirectoryTreeViewItem) ->
item.Populate())
end
//
// From Chapter 16 – DirectoryTreeView
//
type DirectoryTreeView() = class
inherit TreeView() as base
member this.RefreshTree() =
this.BeginInit()
this.Items.Clear()
DriveInfo.GetDrives() |> Seq.iter (fun (drive:DriveInfo) ->
let chDrive = String.get (drive.Name|> String.capitalize) 0
let item = new DirectoryTreeViewItem(drive.RootDirectory)
if (chDrive <> 'A' && chDrive <> 'B' && drive.IsReady && drive.VolumeLabel.Length > 0) then
item.Text <- String.Format("{0} ({1})",drive.VolumeLabel,drive.Name)
else
item.Text <- String.Format("{0} ({1})",drive.DriveType,drive.Name)
if (chDrive = 'A' || chDrive = 'B') then
item.SelectedImage <- floppyImage
item.UnselectedImage <- floppyImage
else if (drive.DriveType = DriveType.CDRom) then
item.SelectedImage <- cddriveImage
item.UnselectedImage <- cddriveImage
else
item.SelectedImage <- driveImage
item.UnselectedImage <- driveImage
//item.Selected.Add(fun _ -> ())
item |> this.Items.Add |> ignore
if (chDrive <> 'A' && chDrive <> 'B' && drive.IsReady) then
item.Populate()
this.EndInit()
)
end
//
// From Chapter 16 – RecurseDirectoriesIncrementally
//
let tree = new DirectoryTreeView()
tree.RefreshTree()
let stack = new StackPanel()
tree.SelectedItemChanged.Add(fun args ->
let item = args.NewValue :?> DirectoryTreeViewItem
stack.Children.Clear()
try
item.DirectoryInfo.GetFiles() |> Seq.iter (fun info ->
let text = new TextBlock(Text=info.Name)
text |> stack.Children.Add |> ignore)
with _ -> ()
)
let grid = new Grid()
[new ColumnDefinition(Width=new GridLength(50.0, GridUnitType.Star));
new ColumnDefinition(Width=GridLength.Auto);
new ColumnDefinition(Width=new GridLength(50.0, GridUnitType.Star))]
|> List.iter grid.ColumnDefinitions.Add
tree |> grid.Children.Add
Grid.SetColumn(tree,0)
let split = new GridSplitter(Width=6.0,
ResizeBehavior = GridResizeBehavior.PreviousAndNext)
split |> grid.Children.Add
Grid.SetColumn(split,1)
let scroll = new ScrollViewer(Content = stack)
scroll |> grid.Children.Add
Grid.SetColumn(scroll,2)
let window = new Window(Title="Recurse Directories Incrementally",
Content=grid)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
TemplateTheTree & DiskDirectory
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
open System
open System.IO
open System.Windows
open System.Windows.Controls
open System.Windows.Data
open System.Windows.Input
open System.Windows.Media
open System.Windows.Media.Imaging
//
// From Chapter 16 – TemplateTheTree & DiskDirectory
//
//----------------------------------------------------------
type DiskDirectory = class
val mutable dirinfo:DirectoryInfo
new (rootdir:DirectoryInfo) as this = {dirinfo=rootdir}
member this.Name
with get() = this.dirinfo
member this.Subdirectories
with get() =
let dirs = new ResizeArray<DiskDirectory>()
try
this.dirinfo.GetDirectories() |> Seq.iter (fun subdir ->
dirs.Add(new DiskDirectory(subdir)))
with _ -> ()
dirs
end
let treevue = new TreeView()
let window = new Window(Title="Template the Tree",
Content=treevue)
let template = new HierarchicalDataTemplate(typeof<DiskDirectory>)
template.ItemsSource <- new Binding("Subdirectories")
let factoryTextBlock = new FrameworkElementFactory(typeof<TextBlock>)
(TextBlock.TextProperty, new Binding("Name"))
|> factoryTextBlock.SetBinding
template.VisualTree <- factoryTextBlock
let dir = new DiskDirectory(new DirectoryInfo(Path.GetPathRoot(Environment.SystemDirectory)))
treevue.Items.Add
(let item = new TreeViewItem(Header=dir.Name,
IsExpanded=true,
ItemsSource=dir.Subdirectories,
ItemTemplate=template)
item)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
ShowClassHierarchy
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
open System
open System.Collections.Generic
open System.Reflection
open System.Windows
open System.Windows.Controls
//
// From Chapter 16 - ShowClassHierarchy
//
//----------------------------------------------------------
type TypeTreeViewItem = class
inherit TreeViewItem as base
val mutable mytype:Type
new () as this = {mytype=null}
member this.Type
with get() = this.mytype
and set (value:Type) =
this.mytype <- value
if (this.mytype.IsAbstract) then
base.Header <- (this.mytype.Name + " (abstract)")
else
base.Header <- this.mytype.Name
end
type ClassHierarchyTreeView = class
inherit TreeView as base
new (typeRoot:Type) as this = {} then
let dummy = new UIElement()
let assemblies = new ResizeArray<Assembly>()
Assembly.GetExecutingAssembly().GetReferencedAssemblies()
|> Seq.iter (fun name -> assemblies.Add(Assembly.Load(name)))
let classes = new SortedList<string,Type>()
classes.Add(typeRoot.Name,typeRoot)
assemblies |> Seq.iter (fun assembly ->
assembly.GetTypes() |> Seq.iter (fun t ->
if (t.IsPublic && t.IsSubclassOf(typeRoot)) then
classes.Add(t.Name,t)))
// create root item
let item = new TypeTreeViewItem(Type=typeRoot)
item |> this.Items.Add |> ignore
// Add recursively
this.CreateLinkedItems item classes
member this.CreateLinkedItems (itemBase:TypeTreeViewItem) (list:SortedList<string,Type>) =
list |> Seq.iter (fun keypair ->
if (keypair.Value.BaseType = itemBase.Type) then
let item = new TypeTreeViewItem(Type=keypair.Value)
itemBase.Items.Add(item) |> ignore
this.CreateLinkedItems item list
)
end
let treevue = new ClassHierarchyTreeView(typeof<System.Windows.Threading.DispatcherObject>)
let window = new Window(Title="Show Class Hierarchy",
Content=treevue)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
ListSystemParameters
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
open System
open System.Collections.Generic
open System.ComponentModel
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Data
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 16 - ListSystemParameters
//
//----------------------------------------------------------
type SystemParam() = class
let mutable strName=""
let mutable objvalue = null
member this.Name
with get() = strName
and set (value:string) = strName <- value
member this.Value
with get() = objvalue
and set (value:obj) = objvalue <- value
override this.ToString() =
this.Name + "=" + this.Value.ToString()
end
let grdvue = new GridView()
let lstvue = new ListView(View=grdvue)
// Create two GridView columns.
new GridViewColumn(Header="Property Name",
Width=200.0,
DisplayMemberBinding = new Binding("Name"))
|> grdvue.Columns.Add
new GridViewColumn(Header="Value",
Width=200.0,
DisplayMemberBinding = new Binding("Value"))
|> grdvue.Columns.Add
typeof<SystemParameters>.GetProperties() |> Seq.iter (fun prop ->
if (prop.PropertyType <> typeof<ResourceKey>) then
new SystemParam(Name=prop.Name,
Value = prop.GetValue(null,null))
|> lstvue.Items.Add |> ignore
)
let window = new Window(Title="List System Parameters",
Content=lstvue)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
ListSortedSystemParameters
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
open System
open System.Collections.Generic
open System.ComponentModel
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Data
open System.Windows.Input
open System.Windows.Media
//
// From Chapter 16 - ListSortedSystemParameters
//
//----------------------------------------------------------
type SystemParam() = class
let mutable strName=""
let mutable objvalue = null
member this.Name
with get() = strName
and set (value:string) = strName <- value
member this.Value
with get() = objvalue
and set (value:obj) = objvalue <- value
override this.ToString() =
this.Name + "=" + this.Value.ToString()
end
let grdvue = new GridView()
let lstvue = new ListView(View=grdvue)
// Create two GridView columns.
new GridViewColumn(Header="Property Name",
Width=200.0,
DisplayMemberBinding = new Binding("Name"))
|> grdvue.Columns.Add
// Create DataTemplate for second column
let template = new DataTemplate(typeof<string>)
let factoryTextBlock = new FrameworkElementFactory(typeof<TextBlock>)
(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Right)
|> factoryTextBlock.SetValue
(TextBlock.TextProperty,new Binding("Value"))
|> factoryTextBlock.SetBinding
template.VisualTree <- factoryTextBlock
new GridViewColumn(Header="Value",
Width=200.0,
CellTemplate=template,
DisplayMemberBinding = new Binding("Value"))
|> grdvue.Columns.Add
let sortlist = new SortedList<string,SystemParam>()
typeof<SystemParameters>.GetProperties() |> Seq.iter (fun prop ->
if (prop.PropertyType <> typeof<ResourceKey>) then
(prop.Name,
new SystemParam(Name=prop.Name,
Value = prop.GetValue(null,null)))
|> sortlist.Add
)
lstvue.ItemsSource <- sortlist.Values
let window = new Window(Title="List Sorted System Parameters",
Content=lstvue)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif
ExploreDependencyProperties
#light
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"classhierarchytreeview.dll"
open System
open System.Collections.Generic
open System.ComponentModel
open System.Globalization
open System.Reflection
open System.Windows
open System.Windows.Controls
open System.Windows.Data
open Chapter16 // for ClassHierarchyTreeView
//
// From Chapter 16 - ExploreDependencyProperties
//
//----------------------------------------------------------
type TypeToString() =
interface IValueConverter with
member v.Convert (o:obj,t:Type,param:obj,culture:CultureInfo) =
(o :?> Type).Name :> obj
member v.ConvertBack (o:obj,t:Type,param:obj,culture:CultureInfo) = null
type MetadataToFlags() =
interface IValueConverter with
member v.Convert (o:obj,t:Type,param:obj,culture:CultureInfo) =
// Need to wrap with try/with for those objects that
// only can cast downto PropertyMetadata
try
let metadata = o :?> FrameworkPropertyMetadata
let checks = [metadata.AffectsMeasure;
metadata.AffectsArrange;
metadata.AffectsParentMeasure;
metadata.AffectsParentArrange;
metadata.AffectsRender;
metadata.Inherits;
metadata.OverridesInheritanceBehavior;
metadata.IsNotDataBindable;
metadata.BindsTwoWayByDefault;
metadata.Journal]
let options = [FrameworkPropertyMetadataOptions.AffectsMeasure;
FrameworkPropertyMetadataOptions.AffectsArrange;
FrameworkPropertyMetadataOptions.AffectsParentMeasure;
FrameworkPropertyMetadataOptions.AffectsParentArrange;
FrameworkPropertyMetadataOptions.AffectsRender;
FrameworkPropertyMetadataOptions.Inherits;
FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior;
FrameworkPropertyMetadataOptions.NotDataBindable;
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault;
FrameworkPropertyMetadataOptions.Journal;]
// Instead of a bunch of if statements, I replaced it with
// zip, filter, map and combine...
let flags =
Seq.zip checks options
|> Seq.filter (fun (check,option) -> check)
|> Seq.map (fun (check,option) -> option)
|> Seq.to_list
|> Enum.combine
flags :> obj
with _ ->
FrameworkPropertyMetadataOptions.None :> obj
member v.ConvertBack (o:obj,t:Type,param:obj,culture:CultureInfo) =
let options = o :?> FrameworkPropertyMetadataOptions
new FrameworkPropertyMetadata(null,options) :> obj
let mutable private initTypeProperty : DependencyProperty = null
type DependencyPropertyListView () = class
inherit ListView() as base
do
let grdvue = new GridView()
base.View <- grdvue
let buildTemplate (bind:Binding) converter =
let elTextBlock =new FrameworkElementFactory(typeof<TextBlock>)
let template = new DataTemplate(VisualTree=elTextBlock)
bind.Converter <- converter
(TextBlock.TextProperty,bind) |> elTextBlock.SetBinding
template
let ownerTemplate = buildTemplate (new Binding("OwnerType")) (new TypeToString())
let flagsTemplate = buildTemplate (new Binding("DefaultMetadata")) (new MetadataToFlags())
let coldefs =
[("Name",Some("Name"),None,150.0);
("Owner",None,Some(ownerTemplate),150.0);
("Default",Some("DefaultMetadata.DefaultValue"),None,75.0);
("Read-Only",Some("DefaultMetadata.ReadOnly"),None,75.0);
("Usage",Some("DefaultMetadata.AttachedPropertyUsage"),None,75.0);
("Flags",None,Some(flagsTemplate),250.0);]
coldefs |> Seq.iter (fun (header,bindingOption,templateOption,width) ->
match bindingOption with
| Some binding ->
new GridViewColumn(Header=header,
DisplayMemberBinding = new Binding(binding),
Width=width)
|> grdvue.Columns.Add
| None -> ()
match templateOption with
| Some template ->
new GridViewColumn(Header=header,
CellTemplate = template,
Width=width)
|> grdvue.Columns.Add
| None -> ())
static member TypeProperty =
if initTypeProperty = null then
let metadata =
new PropertyMetadata(null, new PropertyChangedCallback(DependencyPropertyListView.OnTypePropertyChanged))
initTypeProperty <-
DependencyProperty.Register
("Type",
typeof<Type>,
typeof<DependencyPropertyListView>,
metadata)
initTypeProperty
static member OnTypePropertyChanged (obj:DependencyObject) (args:DependencyPropertyChangedEventArgs) =
let lstvue = obj :?> DependencyPropertyListView
let t = args.NewValue :?> Type
lstvue.ItemsSource <- null
if t <> null then
let list = new SortedList<string,DependencyProperty>()
t.GetFields() |> Seq.iter (fun info ->
if info.FieldType = typeof<DependencyProperty> then
(info.Name,(info.GetValue(null) :?> DependencyProperty))
|> list.Add)
lstvue.ItemsSource <- list.Values
member this.Type
with get() =
let value = this.GetValue(DependencyPropertyListView.TypeProperty)
(value :?> DependencyProperty)
and set (value:DependencyProperty) = this.SetValue(DependencyPropertyListView.TypeProperty,value)
end
let treevue = new Chapter16.ClassHierarchyTreeView(typeof<DependencyObject>)
let grid = new Grid()
// create 3 column definitions for Grid
[new GridLength(1.0,GridUnitType.Star);
GridLength.Auto;
new GridLength(3.0,GridUnitType.Star)]
|> Seq.iter (fun width ->
new ColumnDefinition(Width=width) |> grid.ColumnDefinitions.Add)
treevue |> grid.Children.Add
Grid.SetColumn(treevue,0)
let split = new GridSplitter(HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Stretch,
Width = 6.0)
split |> grid.Children.Add
Grid.SetColumn(split,1)
let lstvue = new DependencyPropertyListView(DataContext=treevue)
lstvue |> grid.Children.Add
Grid.SetColumn(lstvue,2)
(DependencyPropertyListView.TypeProperty, "SelectedItem.Type")
|> lstvue.SetBinding
let window = new Window(Title="Explore Dependency Properties",
Content=grid)
#if COMPILED
[<STAThread()>]
do
let app = Application() in
app.Run(window) |> ignore
#endif