事件 (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#)

事件和委托