事件 (F#)

更新:2011 年 1 月

事件允许您将函数调用与用户操作关联,并且是 GUI 编程的关键所在。 事件也可以由应用程序或操作系统触发。

处理事件

当您使用诸如 Windows 窗体或 Windows Presentation Foundation (WPF) 之类的 GUI 库时,应用程序中的大部分代码都是针对该库预定义的事件运行的。 这些预定义事件是诸如窗体和控件等 GUI 类的成员。 通过引用有意义的特定命名事件(例如,Form 类的 Click 事件)并调用 Add 方法,您可以向预先存在的事件(例如按钮单击)中添加自定义行为,如下面的代码所示。 如果您从 F# Interactive 运行此事件,请忽略对 Run 的调用。

open System.Windows.Forms

let form = new Form(Text="F# Windows Form",
                    Visible = true,
                    TopMost = true)

form.Click.Add(fun evArgs -> System.Console.Beep())
Application.Run(form)

Add 方法的类型为 ('a -> unit) -> unit。 因此,事件处理程序方法将接受一个形参(通常为事件实参),并返回 unit。 前面的示例显示了 lambda 表达式形式的事件处理程序。 事件处理程序也可以为函数值,如下面的代码示例所示。 下面的代码示例还显示了事件处理程序参数的用法,这些参数提供特定于事件类型的信息。 对于 MouseMove 事件,系统将传递 MouseEventArgs 对象,其中包含指针的 X 和 Y 位置。

open System.Windows.Forms

let Beep evArgs =
    System.Console.Beep( )  


let form = new Form(Text = "F# Windows Form",
                    Visible = true,
                    TopMost = true)

let MouseMoveEventHandler (evArgs : System.Windows.Forms.MouseEventArgs) =
    form.Text <- System.String.Format("{0},{1}", evArgs.X, evArgs.Y)

form.Click.Add(Beep)
form.MouseMove.Add(MouseMoveEventHandler)
Application.Run(form)

创建自定义事件

F# 事件由 F# 事件类表示,该类可实现 IEvent 接口。 IEvent 本身是一个接口,它合并了两个其他接口(IObservable<T>IDelegateEvent)的功能。 因此,在其他语言中,Event 具有委托的同等功能,以及来自 IObservable 的附加功能,这意味着 F# 事件支持事件筛选并使用 F# 第一类函数和 lambda 表达式作为事件处理程序。 Event 模块中提供了此功能。

若要像任何其他 .NET Framework 事件一样为某个类创建事件,请向该类添加一个 let 绑定,用于将 Event 定义为类中的字段。 您可以将所需的事件参数类型指定为类型参数,或将其保留为空,让编译器推断出相应的类型。 还必须定义一个将事件公开为 CLI 事件的事件成员。 该成员应具有 CLIEvent 特性。 该成员的声明方式与属性的声明方式相同,实现起来也简单,只需调用该事件的 Publish 属性。 类用户可使用已发布事件的 Add 方法来添加处理程序。 Add 方法的参数可以为 lambda 表达式。 可以使用事件的 Trigger 属性来引发事件,以便将参数传递给处理程序函数。 下面的代码示例阐释了这一点。 在此示例中,事件的推断类型参数是一个元组,它表示 lambda 表达式的参数。

open System.Collections.Generic

type MyClassWithCLIEvent() =

    let event1 = new Event<_>()

    [<CLIEvent>]
    member this.Event1 = event1.Publish

    member this.TestEvent(arg) =
        event1.Trigger(this, arg)

let classWithEvent = new MyClassWithCLIEvent()
classWithEvent.Event1.Add(fun (sender, arg) -> 
        printfn "Event1 occurred! Object data: %s" arg)

classWithEvent.TestEvent("Hello World!")

System.Console.ReadLine() |> ignore

输出如下所示。

Event1 occurred! Object data: Hello World!

此处阐释了 Event 模块提供的附加功能。 下面的代码示例阐释了 Event.create 的基本用法:创建一个事件和一个触发器方法,添加两个 lambda 表达式形式的事件处理程序,然后触发该事件来执行这两个 lambda 表达式。

type MyType() =
    let myEvent = new Event<_>()

    member this.AddHandlers() =
       Event.add (fun string1 -> printfn "%s" string1) myEvent.Publish
       Event.add (fun string1 -> printfn "Given a value: %s" string1) myEvent.Publish

    member this.Trigger(message) =
       myEvent.Trigger(message)

let myMyType = MyType()
myMyType.AddHandlers()
myMyType.Trigger("Event occurred.")

上述代码的输出结果如下。

Event occurred.
Given a value: Event occurred.

处理事件流

您可以在 Event 模块中使用函数,采用高度自定义的方式来处理事件流,而不仅仅是使用 Event.add 函数为事件添加事件处理程序。 为此,可以使用前向管道 (|>) 以及事件作为一系列函数调用中的第一个值,并使用 Event 模块函数作为后续的函数调用。

下面的代码示例显示如何设置仅在某些情况下才会为其调用事件处理程序的事件。

let form = new Form(Text = "F# Windows Form",
                    Visible = true,
                    TopMost = true)
form.MouseMove
    |> Event.filter ( fun evArgs -> evArgs.X > 100 && evArgs.Y > 100)
    |> Event.add ( fun evArgs ->
        form.BackColor <- System.Drawing.Color.FromArgb(
            evArgs.X, evArgs.Y, evArgs.X ^^^ evArgs.Y) )

Observable 模块包含用于操作可观测对象的类似函数。 可观测对象与事件类似,但只有在其本身被订阅时才会主动订阅事件。

请参见

参考

Lambda 表达式:fun 关键字 (F#)

Control.Event 模块 (F#)

Control.Event<'T> 类 (F#)

Control.Event<'Delegate,'Args> 类 (F#)

概念

成员 (F#)

事件和委托

修订记录

日期

修订记录

原因

2011 年 1 月

强调 Event 而不是 DelegateEvent 的用法。

信息补充。