事件 (F#)

事件使您能够将函数调用与用户操作而是重要在 GUI 编程。事件也可以触发应用程序或操作系统。

处理事件

当您使用诸如 windows 窗体或 windows presentation foundation 之类的 GUI 库 (WPF)时,应用程序中的大部分代码运行以响应由库预定义事件。这些预定义事件是 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 表达式作为事件处理程序。此函数在 事件模块提供。

若要创建在象任何其他 .NET framework 事件的类中的事件,请添加到类定义 Event 作为类的一个字段的一 let 绑定。可以指定所需的事件参数类型为类型参数,或将其保留为空白让编译器推断适当的类型。还必须定义一个事件作为 CLI 事件的事件成员。此成员应具有 CLIEvent 属性。声明它与属性类似,并且其实现是调用该事件的 发布 属性。您的类的用户可以使用已发布事件的 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 模块提供的附加功能此处说明。以 lambda 表达式的形式,下面的代码示例阐释 Event.create 的基本使用创建事件和触发器方法,添加两个事件处理程序,然后触发事件执行两个 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.add 功能,可以在 Event 模块可以使用函数处理事件流采用高度自定义的方式。为此,可以处理事件,作为一系列的第一个函数作为函数调用和 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) )

可观测模块 包含对可观测对象的类似功能。,如果它们订阅,可观测对象与事件类似,但时才会主动订阅事件。

实现接口事件

在开发 UI 元素,则可以通过创建新窗体或从现有窗体或控件继承的新控件通常启动。事件在界面经常定义,,因此,在这种情况下,必须实现接口实现事件。INotifyPropertyChanged 接口定义一个 PropertyChanged 事件。下面的代码演示如何实现此继承的接口定义的事件:

module CustomForm

open System.Windows.Forms
open System.ComponentModel

type AppForm() as this =
   inherit Form()

   // Define the propertyChanged event.
   let propertyChanged = Event<PropertyChangedEventHandler, PropertyChangedEventArgs>()
   let mutable underlyingValue = "text0"

   // Set up a click event to change the properties.
   do
      this.Click |> Event.add(fun evArgs -> this.Property1 <- "text2"
                                            this.Property2 <- "text3")

   // This property does not have the property-changed event set.
   member val Property1 : string = "text" with get, set

   // This property has the property-changed event set.
   member this.Property2
        with get() = underlyingValue
        and set(newValue) =
            underlyingValue <- newValue
            propertyChanged.Trigger(this, new PropertyChangedEventArgs("Property2"))

   // Expose the PropertyChanged event as a first class .NET event.
   [<CLIEvent>]
   member this.PropertyChanged = propertyChanged.Publish


   // Define the add and remove methods to implement this interface.
   interface INotifyPropertyChanged with
       member this.add_PropertyChanged(handler) = propertyChanged.Publish.AddHandler(handler)
       member this.remove_PropertyChanged(handler) = propertyChanged.Publish.RemoveHandler(handler)

   // This is the event-handler method.
   member this.OnPropertyChanged(args : PropertyChangedEventArgs) =
       let newProperty = this.GetType().GetProperty(args.PropertyName)
       let newValue = newProperty.GetValue(this :> obj) :?> string
       printfn "Property %s changed its value to %s" args.PropertyName newValue

// Create a form, hook up the event handler, and start the application.
let appForm = new AppForm()
let inpc = appForm :> INotifyPropertyChanged
inpc.PropertyChanged.Add(appForm.OnPropertyChanged)
Application.Run(appForm)

如果要安装在构造函数的事件,代码较为复杂,因为事件挂接在其他构造函数必须在 then 块,如下面的示例所示:

module CustomForm

open System.Windows.Forms
open System.ComponentModel

// Create a private constructor with a dummy argument so that the public
// constructor can have no arguments.
type AppForm private (dummy) as this =
   inherit Form()

   // Define the propertyChanged event.
   let propertyChanged = Event<PropertyChangedEventHandler, PropertyChangedEventArgs>()
   let mutable underlyingValue = "text0"

   // Set up a click event to change the properties.
   do
      this.Click |> Event.add(fun evArgs -> this.Property1 <- "text2"
                                            this.Property2 <- "text3")
      

   // This property does not have the property changed event set.
   member val Property1 : string = "text" with get, set

   // This property has the property changed event set.
   member this.Property2
        with get() = underlyingValue
        and set(newValue) =
            underlyingValue <- newValue
            propertyChanged.Trigger(this, new PropertyChangedEventArgs("Property2"))

   [<CLIEvent>]
   member this.PropertyChanged = propertyChanged.Publish

   // Define the add and remove methods to implement this interface.
   interface INotifyPropertyChanged with
       member this.add_PropertyChanged(handler) = this.PropertyChanged.AddHandler(handler)
       member this.remove_PropertyChanged(handler) = this.PropertyChanged.RemoveHandler(handler)

   // This is the event handler method.
   member this.OnPropertyChanged(args : PropertyChangedEventArgs) =
       let newProperty = this.GetType().GetProperty(args.PropertyName)
       let newValue = newProperty.GetValue(this :> obj) :?> string
       printfn "Property %s changed its value to %s" args.PropertyName newValue

   new() as this =
        new AppForm(0)
          then
          let inpc = this :> INotifyPropertyChanged
          inpc.PropertyChanged.Add(this.OnPropertyChanged)
       

// Create a form, hook up the event handler, and start the application.
let appForm = new AppForm()
Application.Run(appForm)

请参见

参考

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

Control.Event 模块 (F#)

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

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

其他资源

成员 (F#)

事件和委托