共用方式為


事件

事件可讓您將函式呼叫與用戶動作產生關聯,而且在 GUI 程式設計中很重要。 事件也可以由您的應用程式或作系統觸發。

處理事件

當您使用像是 Windows Forms 或 Windows Presentation Foundation (WPF) 的 GUI 連結庫時,應用程式中的大部分程式代碼都會執行,以響應連結庫預先定義的事件。 這些預先定義的事件是 GUI 類別的成員,例如表單和控件。 您可以參考感興趣的特定具名事件(例如 Click 類別的事件 Form )並叫 Add 用 方法,將自定義行為新增至預先存在的事件,例如按鍵,如下列程式碼所示。 如果您從 F# Interactive 執行此專案,請省略 對 System.Windows.Forms.Application.Run(System.Windows.Forms.Form)的呼叫。

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針對事件,系統會傳遞 System.Windows.Forms.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# 事件是由實作 IEvent 介面的 F# 事件類型表示。 IEvent本身是結合其他兩個介面和 IDelegateEvent 功能的介面System.IObservable<'T>。 因此, Event具有其他語言委派的對等功能,加上 來自的其他功能 IObservable,這表示 F# 事件支援事件篩選,並使用 F# 一流的函式和 Lambda 運算式作為事件處理程式。 這項功能會在 事件模組中提供。

若要在類似任何其他 .NET Framework 事件的類別上建立事件,請將定義 Event 為 類別中字段的系結新增至 類別let。 您可以將所需的事件自變數類型指定為類型自變數,或將它保留空白,並讓編譯程式推斷適當的類型。 您也必須定義將事件公開為 CLI 事件的事件成員。 此成員應具有 CLIEvent 屬性。 它宣告為屬性,而其實作只是呼叫事件的 Publish 屬性。 類別的使用者可以使用 Add 已發佈事件的 方法來新增處理程式。 方法的 Add 自變數可以是 Lambda 運算式。 您可以使用 Trigger 事件的 屬性來引發 事件,並將自變數傳遞至處理程式函式。 下列程式代碼範例說明這點。 在此範例中,事件的推斷型別自變數是 Tuple,代表 Lambda 表達式的自變數。

open System.Collections.Generic

type MyClassWithCLIEvent() =

    let event1 = new Event<string>()

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

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

let classWithEvent = new MyClassWithCLIEvent()
classWithEvent.Event1.Add(fun 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 模組包含對可觀察物件運作的類似函式。 可觀察的物件與事件類似,但只有在事件本身已訂閱時,才會主動訂閱事件。

實作介面事件

當您開發 UI 元件時,通常會從建立繼承自現有表單或控件的新表單或新控件開始。 事件經常在介面上定義,在此情況下,您必須實作 介面來實作 事件。 介面 System.ComponentModel.INotifyPropertyChanged 會定義單 System.ComponentModel.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 {args.PropertyName} changed its value to {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 {args.PropertyName} changed its value to {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)

另請參閱