F# 交互中的 Windows UI(UWP 或 8.1)

Windows UI (UWP or 8.1) in F# interactive

通过引用默认的 WPF DLL,使用纯代码 WPF 可以很容易地做任何事情:

#r "PresentationCore.dll"
#r "PresentationFramework.dll"
// ...other DLLs...
#r "WindowsBase.dll"

let window = System.Windows.Window()
let panel = System.Windows.Controls.StackPanel()
let button = System.Windows.Controls.Button()
panel.Children.Add button
button.Content <- "hi"

window.Content <- panel

window.Show()

...并且您可以在 window 仍处于打开状态时对其进行操作...

button.Click.Add (fun _ ->
    button.Content <-
        button.Content :?> string |> fun x -> (x + "!") :> obj)

...然后单击按钮以查看它是否有效。这似乎是构建 UI 组件的一种非常强大的方法。

有没有什么方法可以用 Windows.UI namespace/controls/UI 框架做同样的事情——在 F# 交互中加载一些程序集并实例化 UI 上的组件飞?

我曾天真地尝试引用看似相关的文件:

#r @"C:\Program Files (x86)\Windows Kits\References\Windows.Foundation.UniversalApiContract.0.0.0\Windows.Foundation.UniversalApiContract.winmd"
#r @"C:\Program Files (x86)\Windows Kits\References\Windows.Foundation.FoundationContract.0.0.0\Windows.Foundation.FoundationContract.winmd"

...这样做让我对 Windows.UI 命名空间有了智能感知。但是当我尝试实例化某些东西时:

Windows.UI.Xaml.Application()

我得到:

error FS0193: Could not load file or assembly 'file:///C:\Program Files (x86)\Windows Kits\References\Windows.Foundation.UniversalApiContract.0.0.0\Windows.Foundation.UniversalApiContract.winmd' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)

我的理解是 F# 还没有对 UWP 的支持。
例如看这个新鲜的 open issue.

编译器不支持 WinRT 程序集,因此您将无法在尝试引用程序集和干净地使用其中的类型时引用程序集。

另一方面...由于 .NET 运行时具有对 WinRT 类型的本机支持,您可以使用反射来加载这些类型并访问它们的成员。通过大量的努力,您甚至可以构建一个类型提供程序来为该反射提供一个干净的外观,并使其看起来好像您可以直接使用这些类型。下面是一个如何通过反射从 F# 直接调用 WinRT API 的小示例:

open System.Reflection

let (?) (o:obj) s : 'a =
    let rec build ty args =
        if Reflection.FSharpType.IsFunction ty then
            let dom, rng = Reflection.FSharpType.GetFunctionElements ty
            let mkArgs =
                if dom = typeof<unit> then
                    if Reflection.FSharpType.IsFunction rng then failwith "Unit as non-final argument in curried definition?"
                    fun _ -> args
                else
                    fun arg -> arg::args
            Reflection.FSharpValue.MakeFunction(ty, fun o -> build rng (mkArgs o))
        else
            let rcvr,ty,flags =
                match o with
                | :? System.Type as ty -> null,ty,BindingFlags.Static
                | _ -> o,o.GetType(),BindingFlags.Instance
            let flags = flags ||| BindingFlags.Public
            let meth =
                if Reflection.FSharpType.IsFunction typeof<'a> then
                    query {
                        for m in ty.GetMethods(flags) do
                        where (m.Name = s)
                        where (m.GetParameters().Length = args.Length)
                        exactlyOne
                    }                    
                else
                    ty.GetProperty(s, flags).GetGetMethod()
            meth.Invoke(rcvr, args |> List.toArray)

    build typeof<'a> [] :?> 'a

let Clipboard = System.Type.GetType(@"Windows.ApplicationModel.DataTransfer.Clipboard, Windows.ApplicationModel, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime")

Clipboard?GetContent()?AvailableFormats |> Seq.iter (printfn "%s")