F#异步显示WPF窗口

5
这个问题让我抓狂了。大致情况如下:
我有一个解决方案中的两个项目:第一个是F#控制台应用程序,第二个是C#库,其中有一个名为DisplayWindow的C#+XAML类,该类继承自WPF窗口。 DisplayWindow有一个方法public void SetMessage(string s) {...},使窗口以大而光亮的字母显示传递给它的文本,可能还会闪烁和旋转等WPF擅长的一切。
问题是:从我的F#程序中,我需要创建一个函数let openAWindow text = ???,以便每次使用文本调用它时都会异步打开一个新的DisplayWindow。最佳方法是什么?使用async {}还是System.Threading.Thread?感谢您的帮助 :)
编辑:我发现了这篇博客文章http://deanchalk.com/2010/10/08/f-interacting-with-wpf-dispatcher-via-f-interactive-window/,它可以工作,但有时会导致ArgumentException并显示错误文本“已存在具有相同键的条目。”,所以我不知道发生了什么 :(

我猜你确实有一个调用Application.Run的主UI线程?如果是这样:如果你需要从其他线程访问UI元素,请按照此答案中所述进行操作:http://stackoverflow.com/questions/1115132/stathread-as-async-workflow-in-f - wmeyer
@wmeyer 是的,我看过了。但我找不到 Async.SwitchToGuiThread 方法,只有 SwitchToContext、SwitchToNewThread 和 SwitchToThreadPool。 - Ed Ayers
@Ciemnl SwitchToContext 应该可以解决问题。你试过了吗? - Joh
1个回答

8
我会翻译中文。这段内容和编程有关,作者提到了他为他们的可视化库 F# for Visualization 做了这件事,然后在他的书 Visual F# 2010 for Technical Computing 中描述了他使用的技术。
首先,我编写了一个懒惰的 thunk,在强制求值时初始化 WPF(包括 STA UI 线程和 Application)。
> let ui =
    let mk() =
      let wh = new ManualResetEvent(false)
      let application = ref null
      let start() =
        let app = Application()
        application := app
        ignore(wh.Set())
        app.Run() |> ignore
    let thread = Thread start
    thread.IsBackground <- true
    thread.SetApartmentState ApartmentState.STA
    thread.Start()
    ignore(wh.WaitOne())
    !application, thread
  lazy(mk());;
val ui : Lazy<Application * Thread> = <unevaluated>

然后我编写了一个spawn函数,它将函数f应用于参数x并在UI线程上运行。
> let spawn : ('a -> 'b) -> 'a -> 'b =
    fun f x ->
      let app, thread = ui.Force()
      let f _ =
        try
          let f_x = f x
          fun () -> f_x
        with e ->
          fun () -> raise e
      let t = app.Dispatcher.Invoke(DispatcherPriority.Send, System.Func<_, _>(f), null)
      (t :?> unit -> 'b)();;
val spawn : ('a -> 'b) -> 'a -> 'b

现在只需要在UI线程上调用您的openAWindow函数即可:
let openAWindow text =
  DisplayWindow().SetMessage text

spawn openAWindow text

1
谢谢你的回答,它似乎有效。我有点新手,不太明白 spawn 在做什么... 我可以看到它让你传递的函数在 UI 线程中运行,但是 let f _ =(t :?> unit -> 'b)() 的原因是什么? - Ed Ayers
1
它会忽略通过 Invoke 传递的参数,并返回一个函数而不是一个值。然后在当前线程上评估该函数,可能返回一个值或引发异常。 - J D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接