F# CTP and Silverlight 2 and XAML
Following up from yesterday, the rudiments of the clock, to show XAML integration in the F# plus Silverlight combination. This should be enough of a template for starting with and mutating to taste.
The code is unchanged in the Feb 2010 (v 1.9.9.9) CTP.
astroclock.xaml
:
<UserControl x:Class="astroclock.fs.Page" | |
xmlns="http://schemas.microsoft.com/client/2007" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
Width="480" Height="400"> | |
<Grid x:Name="LayoutRoot"> | |
<Canvas Name="canvas1" Margin="0,0,0,129" Background="Black" Width="480" Height="240"> | |
<TextBlock Height="30" FontSize="25" Canvas.Left="240" Canvas.Top="24" Name="hms" Foreground="White" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" Width="240">::</TextBlock> | |
<TextBlock Height="30" FontSize="25" Canvas.Left="240" Canvas.Top="78" Name="day" Foreground="White" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" Width="240">::</TextBlock> | |
<TextBlock Height="30" FontSize="25" Canvas.Left="240" Canvas.Top="132" Name="date" Foreground="White" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" Width="240">::</TextBlock> | |
<TextBlock Height="30" FontSize="25" Canvas.Left="240" Canvas.Top="186" Name="sunup" Foreground="White" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" Width="240">::</TextBlock> | |
<Line | |
Name="hour" | |
X1="120" Y1="120" | |
X2="120" Y2="60" | |
Stroke="White" | |
StrokeThickness="4"> | |
<Line.RenderTransform> | |
<TransformGroup> | |
<RotateTransform Angle="0" CenterX="120" CenterY="120" /> | |
</TransformGroup> | |
</Line.RenderTransform> | |
</Line> | |
<Line | |
Name="minute" | |
X1="120" Y1="120" | |
X2="120" Y2="30" | |
Stroke="White" | |
StrokeThickness="3"> | |
<Line.RenderTransform> | |
<TransformGroup> | |
<RotateTransform Angle="0" CenterX="120" CenterY="120" /> | |
</TransformGroup> | |
</Line.RenderTransform> | |
</Line> | |
<Line | |
Name="second" | |
X1="120" Y1="120" | |
X2="120" Y2="35" | |
Stroke="White" | |
StrokeThickness="1"> | |
<Line.RenderTransform> | |
<TransformGroup> | |
<RotateTransform Angle="0" CenterX="120" CenterY="120" /> | |
</TransformGroup> | |
</Line.RenderTransform> | |
</Line> | |
</Canvas> | |
<HyperlinkButton Height="28" HorizontalAlignment="Left" Margin="0,0,0,17" Name="hyperlink" VerticalAlignment="Bottom" Width="87" NavigateUri="astroclock.html" Content="Permalink" /> | |
</Grid> | |
</UserControl> |
which we note has a UserControl
as root, and astroclock.fs
:
#light | |
namespace astroclock.fs | |
open System | |
open System.ComponentModel | |
open System.Globalization | |
open System.Windows | |
open System.Windows.Browser | |
open System.Windows.Controls | |
open System.Windows.Markup | |
open System.Windows.Media | |
open System.Windows.Shapes | |
type Page = class | |
inherit UserControl | |
val mutable page : string | |
val mutable query : System.Collections.Generic.IDictionary< string,string > | |
val animate : BackgroundWorker | |
val mutable culture : CultureInfo | |
val mutable hms : string | |
val mutable date : string | |
member this.everySecond() = | |
System.Threading.Thread.Sleep(1000) | |
this.animate.ReportProgress(0) | |
this.everySecond() | |
member this.moveHand name angle = | |
let line = this.FindName(name) :?> Line | |
let tg = line.RenderTransform :?> TransformGroup | |
let rotate = tg.Children.[0] :?> RotateTransform | |
rotate.Angle <- angle | |
member this.setText name value = | |
let block = this.FindName(name) :?> TextBlock | |
block.Text <- value | |
block | |
member this.ignore x = | |
() | |
member this.updateTick() = | |
let now = System.DateTime.Now | |
this.setText "hms" (now.ToString(this.hms, this.culture)) |> this.ignore | |
this.setText "day" (now.ToString("dddd", this.culture)) |> this.ignore | |
this.setText "date" (now.ToString(this.date, this.culture)) |> this.ignore | |
try | |
this.moveHand "second" (6.0 * float(now.Second)) | |
this.moveHand "minute" (float(6*now.Minute)+(0.1*float(now.Second))) | |
this.moveHand "hour" (float(30*now.Hour)+(float(now.Minute)/2.0)+(float(now.Second)/300.0)) | |
with x -> // debug technique | |
(this.setText "sunup" x.Message).FontSize <- 8.0 | |
new () as this = {page = String.Empty; | |
query = null; | |
animate = new BackgroundWorker(); | |
culture = null | |
hms = "HH:mm:ss"; | |
date = "d-MMM-yyyy"; | |
} then | |
System.Windows.Application.LoadComponent(this, new System.Uri("/astroclock.xaml", System.UriKind.Relative)); | |
this.query <- System.Windows.Browser.HtmlPage.Document.QueryString | |
try | |
let culture_name = this.query.["locale"] | |
this.culture <- new CultureInfo(culture_name) | |
with _ -> | |
this.culture <- CultureInfo.CurrentCulture | |
try | |
this.hms <- this.query.["hms"] | |
with _ -> | |
this.hms <- this.culture.DateTimeFormat.LongTimePattern // HH:mm:ss | |
try | |
this.date <- this.query.["date"] | |
with _ -> | |
this.date <- this.culture.DateTimeFormat.ShortDatePattern // d-MMM-yyyy | |
let button = this.FindName("hyperlink") :?> HyperlinkButton | |
button.NavigateUri <- System.Windows.Browser.HtmlPage.Document.DocumentUri | |
this.page <- System.Windows.Browser.HtmlPage.Document.DocumentUri.GetComponents( | |
System.UriComponents.SchemeAndServer ||| System.UriComponents.Path, | |
System.UriFormat.SafeUnescaped) | |
this.animate.WorkerReportsProgress <- true | |
this.animate.DoWork.Add(fun _ -> this.everySecond()) | |
this.animate.ProgressChanged.Add(fun _ -> this.updateTick()) | |
this.animate.RunWorkerAsync() | |
this.updateTick () | |
end | |
type MyApp = class | |
inherit Application | |
new () as this = {} then | |
this.Startup.Add(fun _ -> this.RootVisual <- new Page()) | |
end |
with the obvious AppManifest.xaml
:
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
RuntimeVersion="2.0.31005.0" | |
EntryPointAssembly="astroclock-fs" | |
EntryPointType="astroclock.fs.MyApp"> | |
<Deployment.Parts> | |
<AssemblyPart x:Name="astroclock-fs" Source="astroclock-fs.dll" /> | |
<AssemblyPart x:Name="FSharp.Core" Source="FSharp.Core.dll" /> | |
</Deployment.Parts> | |
</Deployment> |
LATER: Data binding sort of works sometimes; I can get it to work when set at run-time to bind a string
to a TextBlock
via myTextBlock.DataContext <- this.StringProperty
(but not yet through XAML), and Slider
s are being totally recalcitrant...
I think there is some autogeneration magic just not happening for F# -- the best I can get seems to be to approximate a one-time binding -- so it's back to wiring up events by hand.
1 comment :
Hi Steve.
I like what you're doing here.
I plan to try it.
Thanks,
Art Scott
Post a Comment