かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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

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