WPF的F#异步事件处理程序,类似于C#的async和await [英] F# asynchronous event handlers for WPF similar to C#'s async and await

查看:406
本文介绍了WPF的F#异步事件处理程序,类似于C#的async和await的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何用F#编写一个异步WPF(或Windows窗体)事件处理程序的代码?具体来说,是否有任何近似C#5异步和等待的编码模式?

How does one code an asynchronous WPF (or Windows Forms) event handler in F#? Specifically, is there any coding pattern that approximates C# 5's async and await?

这是一个完整的C#WPF应用程序:

Here is a complete C# WPF app:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

class Program
{
    static int IncrementSlowly(int previous)
    {
        Thread.Sleep(3000);
        if (previous == 2) throw new Exception("Oops!");
        return previous + 1;
    }

    static async void btn_Click(object sender, RoutedEventArgs e)
    {
        var btn = sender as Button;
        btn.IsEnabled = false;
        try
        {
            var prev = (int)btn.Content;
            btn.Content = await Task.Run(() => IncrementSlowly(prev));
        }
        catch (Exception ex)
        {
            btn.Content = ex.Message;
        }
        finally
        {
            btn.IsEnabled = true;
        }
    }

    [STAThread]
    static void Main(string[] args)
    {
        var btn = new Button() { Content = 0 };
        var win = new Window() { Content = btn };
        btn.Click += btn_Click;
        new Application().Run(win);
    }
}

我很难弄清楚这将是什么使用F#。我已经尝试了使用异步工作流程和异步方法的组合。它只是很快变得非常混乱。我希望有一种简单的方法可以忽略我。

I am having trouble figuring out what the equivalent would be using F#. I have made several attempts using combinations of async workflows and Async methods. It just gets really messy real fast. I'm hoping there is an easy way that I'm just overlooking.

这是我的起点,将UI锁定在 btn .Content<-递增缓慢地上一个。我接下来要做什么?

Here is my starting point, which locks up the UI at btn.Content <- incrementSlowly prev. What do I do next?

open System
open System.Threading
open System.Threading.Tasks
open System.Windows
open System.Windows.Controls

let incrementSlowly previous = 
    Thread.Sleep(3000)
    if previous = 2 then failwith "Oops!"
    previous + 1

let btn_Click (sender : obj) e = 
    let btn = sender :?> Button
    btn.IsEnabled <- false
    try 
        try 
            let prev = btn.Content :?> int
            btn.Content <- incrementSlowly prev
        with ex -> btn.Content <- ex.Message
    finally
        btn.IsEnabled <- true

[<EntryPoint>][<STAThread>]
let main _ = 
    let btn = new Button(Content = 0)
    let win = new Window(Content = btn)
    btn.Click.AddHandler(RoutedEventHandler(btn_Click))
    Application().Run(win)

顺便说一句,假设缓慢增加无法修改。

By the way, assume that incrementSlowly cannot be modified.

推荐答案

第一步是使 IncrementSlowly 异步。在您的C#代码中,这实际上是同步的,这可能不是一个好主意-在现实情况下,这可能与网络通信,因此通常实际上可以是异步的:

The first step is to make incrementSlowly asynchronous. This is actually synchronous in your C# code, which is probably not a good idea - in a realistic scenario, this could be communicating with network, so very often this can actually be asynchronous:

let incrementSlowly previous = async {
  do! Async.Sleep(3000)
  if previous = 2 then failwith "Oops!"
  return previous + 1 }

现在,您可以使按钮单击处理程序也异步了。稍后,我们将使用 Async.StartImmediate 来启动它,以确保我们可以访问UI元素,因此我们现在不必担心Dispatechers或UI线程:

Now, you can make the button click handler also asynchronous. We'll start it using Async.StartImmediate later to make sure that we can access UI elements, so we do not have to worry about dispatechers or UI threads for now:

let btn_Click (sender : obj) e = async {
  let btn = sender :?> Button
  btn.IsEnabled <- false
  try 
    try 
      let prev = btn.Content :?> int
      let! next = incrementSlowly prev
      btn.Content <- next
    with ex -> btn.Content <- ex.Message
  finally
    btn.IsEnabled <- true }

最后一步是更改事件注册。这样的事情应该可以解决问题:

The final step is to change the event registration. Something like this should do the trick:

btn.Click.Add(RoutedEventHandler(fun sender e ->
  btn_Click sender e |> Async.StartImmediate)

关键是 Async .StartImmediate 启动异步工作流,当我们在UI线程上调用它时,它确保所有实际工作都在UI线程上完成(除非您将其显式卸载到后台),因此安全地访问代码中的UI元素。

The key thing is Async.StartImmediate which starts the asynchronous workflow. When we call this on the UI thread, it ensures that all the actual work is done on the UI thread (unless you offload it explicitly to background) and so it is safe to access UI elements in your code.

这篇关于WPF的F#异步事件处理程序,类似于C#的async和await的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆