かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

F#でWPFの画面構築DSL

タイトル通りのものを作ってみようと思ってもんもんとしています。ただ、DataTemplateとかControlTemplateとかは、ちょっとしんどいので対象外にしようかな〜と思いつつ、ItemsControlのItemTemplateとかくらいは設定できないと使い物にならないよな〜と思ったりしている今日この頃です。

というか、Visual StudioのF#プロジェクトで普通にXAMLとF#のコードビハインドの形のものをサポートしてくれればいいんですけどね!
ということで、まだまだ完成というには程遠いですが、適当に作ってみたもののコードだけ晒してみます。

open System
open System.Windows
open System.Windows.Controls
open System.Windows.Data

module WPFDsl =
    let init<'T when 'T :> DependencyObject> (f : 'T -> unit) (o : 'T) =
        f o
        o

    let children<'T when 'T :> Panel> (children : seq<_>) (panel : 'T) =
        children |> Seq.iter (fun ch -> panel.Children.Add(ch) |> ignore)
        panel

    let attachedProperty<'T when 'T :> DependencyObject> (prop : DependencyProperty) value (o : 'T) =
        o.SetValue(prop, value)
        o

    let (@@=) (prop : DependencyProperty) value = 
        attachedProperty prop value

    let bind<'T when 'T :> FrameworkElement> (prop : DependencyProperty) (b : BindingBase) (o : 'T) =
        o.SetBinding(prop, b) |> ignore
        o
        


open WPFDsl

let mainWindow = 
    Window(
        Title = "Hello", 
        Content = (Grid() 
                    |> init 
                        (fun g -> 
                            g.RowDefinitions.Add <| RowDefinition(Height = GridLength.Auto)
                            g.RowDefinitions.Add <| RowDefinition(Height = GridLength.Auto)
                            g.RowDefinitions.Add <| RowDefinition(Height = GridLength.Auto)
                        )
                    |> children
                        [
                            Button()
                                |> (Grid.RowProperty @@= 0)
                                |> (bind Button.ContentProperty <| Binding("Name"))
                            Button(Content = "OK2")
                                |> (Grid.RowProperty @@= 1)
                            Button(Content = "OK3")
                                |> (Grid.RowProperty @@= 2)
                        ])
    )

type Person = { Name : string }

[<STAThread>]
do
    let app = Application()
    mainWindow.DataContext <- { Name = "田中 太郎" }
    app.Run(mainWindow) |> ignore

まだまだ、書いてて気持ちよくない。