I recently picked up a copy of the book Silverlight 2 Recipes and wanted to try out some of the recipes by building F# version of them. One of the first recipes that caught my eye was recipe 2.6 which talks about working with IsolatedStorage. One of the main issues I had with implementing this recipe in F# is the fact that the public methods for IsolatedStorageFile is different between .NET Framework 3.5 and for Silverlight 2. Therefore, I was unable to call the CreateFile method of IsolatedStorageFile . But thankfully, this problem was already resolved by Michael Giagnocavo in his blog on F# 1.9.6.2 and Silverlight 2. All I had to do is goto Project->[ProjectName] Properties –> Build and enter the following flag in “Other flags:”
--cliroot "C:\Program Files\Microsoft Silverlight\2.0.31005.0"
After configuring the ––cliroot
flag, the source file compiled without problems. Here’s the F# implementation of recipe 2.6 from the book:
#light
namespace SilverLightFSharp
open System.IO
open System.IO.IsolatedStorage
open System.Windows
open System.Windows.Controls
open System.Windows.Media
open System.ServiceModel
open List
// Recipe 2-6 from Silverlight 2 Recipes book
// Persisting Data on the Client
type MyPage() = class
inherit UserControl()
let settings = IsolatedStorageSettings.ApplicationSettings
let setting = "MySettings"
let filename = "FormFields.data"
let dataDirectory = "FormData"
let filepath = System.IO.Path.Combine(dataDirectory, filename)
//new () as this = {} then
do
let buildgrid color =
let grid = new Grid(Background = new SolidColorBrush(color))
let star = GridUnitType.Star
// Add column definitions
[0.06;0.455;0.485]
|> map (fun w -> new ColumnDefinition(Width=new GridLength(w,star)))
|> iter grid.ColumnDefinitions.Add
// Add row definitions
[0.08;0.217;0.61;0.093]
|> map (fun h -> new RowDefinition(Height=new GridLength(h,star)))
|> iter grid.RowDefinitions.Add
grid
let grid = buildgrid Colors.Gray
let gridadd (ui:#UIElement) row col =
grid.Children.Add(ui)
Grid.SetRow(ui,row)
Grid.SetColumn(ui,col)
let sp = new StackPanel(HorizontalAlignment = HorizontalAlignment.Stretch,
Margin=new Thickness(8.0,8.0,10.0,8.0))
new TextBlock(Text="Enter Setting Value",
TextWrapping=TextWrapping.Wrap,
Margin=new Thickness(4.0,4.0,4.0,4.0))
|> sp.Children.Add
let textdata = new TextBox(Height=126.0,
Text="",
TextWrapping=TextWrapping.Wrap,
Margin=new Thickness(4.0,4.0,4.0,4.0))
textdata |> sp.Children.Add
gridadd sp 2 1
// Build Form
let buildform () =
let brush = new LinearGradientBrush
(StartPoint = new Point(0.439999997615814,0.996999979019165),
EndPoint = new Point(0.560000002384186,0.00300000002607703))
[((255uy,58uy,108uy,87uy),0.0);
((255uy,163uy,189uy,163uy),0.536);
((255uy,58uy,108uy,87uy),0.968999981880188)]
|> map (fun (color,offset) -> new GradientStop(Color=(color |> Color.FromArgb), Offset=offset))
|> iter brush.GradientStops.Add
let form = new StackPanel(HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Margin = new Thickness(4.0,4.0,4.0,4.0))
let border = new Border
(HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(8.0,8.0,10.0,8.0),
VerticalAlignment = VerticalAlignment.Stretch,
CornerRadius = new CornerRadius(10.0,10.0,10.0,10.0),
Child=form,
Background=brush)
gridadd border 2 2
Grid.SetRowSpan(border,2)
let createLabel label = new TextBlock(Text=label,
TextWrapping=TextWrapping.Wrap,
Margin = new Thickness(2.0,4.0,2.0,0.0))
let createTextbox () = new TextBox(TextWrapping=TextWrapping.Wrap,
Margin = new Thickness(2.0,0.0,2.0,2.0))
let formelements =
["First Name:";"Last Name:"; "Company:"; "Title:"]
|> map (fun label -> (createLabel label, createTextbox ()))
formelements |> iter (fun (label,tb) ->
form.Children.Add(label)
form.Children.Add(tb))
formelements
let createbtn thickness row col content fn =
let btn = new Button(HorizontalAlignment = HorizontalAlignment.Stretch,
Margin=thickness,
Content=content)
btn.Click.Add(fn)
btn.Click.Add(fun evt -> ())
gridadd btn row col
let form = buildform()
let fields = form |> map (fun (label,tb) -> tb)
// SaveFormData_Click
createbtn (new Thickness(8.0)) 1 1 "Save Form Data"
<| (fun evt ->
try
use store = IsolatedStorageFile.GetUserStoreForApplication()
store.CreateDirectory(dataDirectory)
use stream = store.CreateFile(filepath)
use writer = new StreamWriter(stream)
writer.AutoFlush <- true
let data = fields |> map (fun field -> field.Text + "|")
|> fold_left (+) ""
data |> writer.WriteLine
textdata.Text <- "Data saved to : " + filepath + "\n" + data
with
| :? IsolatedStorageException as ex -> textdata.Text <- "Error saving setting: " + ex.Message
| _ -> textdata.Text <- "Unable to open file... ")
// ReadFormData_Click
createbtn (new Thickness(8.0)) 1 2 "Load Form Data"
<| (fun evt ->
try
use store = IsolatedStorageFile.GetUserStoreForApplication()
use file = new IsolatedStorageFileStream(filepath,FileMode.Open, store)
use reader = new StreamReader(file)
let storedvalues = reader.ReadToEnd();
let data = storedvalues.Split([|'|'|])
|> Array.to_seq |> Seq.take (List.length fields)
|> Seq.to_list
zip fields data |> iter (fun (tb,data) -> tb.Text <- data)
// For checking...
textdata.Text <- storedvalues
with
| :? IsolatedStorageException as ex -> textdata.Text <- "Error saving setting: " + ex.Message
| _ -> textdata.Text <- "Unable to open file... ")
// ButtonUpdateSetting - repurposed for diagnostics
createbtn (new Thickness(4.0,4.0,14.0,4.0)) 3 1 "Update Setting"
<| (fun evt ->
use store = IsolatedStorageFile.GetUserStoreForApplication()
let dirs = store.GetDirectoryNames("*")
let files = store.GetFileNames("*")
let dirtext = "Directories = " + (dirs |> Array.to_list |> map (fun t -> "\n"+t) |>fold_left (+) "")
let filetext = "Filenames = " + (files |> Array.to_list |> map (fun t -> "\n"+t) |>fold_left (+) "")
textdata.Text <- dirtext + "\n" + filetext)
base.Width <- 400.0
base.Height <- 300.0
base.Content <- grid
// Loaded...
try
if settings.Keys.Count <> 0 then
textdata.Text <- settings.[setting].ToString()
with _ as ex -> textdata.Text <- "Error saving setting: " + ex.Message
end
type MyApp = class
inherit Application
new () as this = {} then
this.Startup.Add(fun _ -> this.RootVisual <- new MyPage())
//base.Exit.Add( fun _ -> ()) //this.Application_Exit)
//this.InitializeComponent()
end