Acara

Kejadian memungkinkan Anda untuk mengasosiasikan panggilan fungsi dengan tindakan pengguna dan merupakan hal penting dalam pemrograman GUI. Kejadian juga dapat dipicu oleh aplikasi Anda atau oleh sistem operasi.

Menangani Peristiwa

Saat Anda menggunakan pustaka GUI seperti Formulir Windows atau Windows Presentation Foundation (WPF), sebagian besar kode di aplikasi Anda berjalan sebagai respons terhadap kejadian yang telah ditentukan sebelumnya oleh pustaka. Kejadian yang telah ditentukan sebelumnya ini merupakan anggota dari kelas GUI, seperti formulir dan kontrol. Anda dapat menambahkan perilaku kustom ke kejadian yang sudah ada sebelumnya, seperti klik tombol, dengan merujuk ke kejadian tertentu yang disebut menarik (misalnya, kejadian Click kelas Form) dan memanggil metode Add, seperti yang ditunjukkan dalam kode berikut. Jika Anda menjalankan ini dari F# Interactive, hapus panggilan ke 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)

Jenis metode Add adalah ('a -> unit) -> unit. Oleh karena itu, metode penanganan aktivitas mengambil satu parameter, biasanya adalah argumen kejadian, dan mengembalikan unit. Contoh sebelumnya menunjukkan penanganan aktivitas sebagai ekspresi lambda. Penanganan aktivitas juga dapat berupa nilai fungsi, seperti dalam contoh kode berikut. Contoh kode berikut juga menunjukkan penggunaan parameter penanganan aktivitas, yang memberikan informasi khusus untuk jenis kejadian. Untuk kejadian MouseMove, sistem meneruskan objek System.Windows.Forms.MouseEventArgs, yang berisi posisi X dan Y pointer.

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)

Membuat Kejadian Kustom

Kejadian F# diwakili oleh jenis Kejadian F#, yang mengimplementasikan antarmuka IEvent. IEvent sendiri adalah antarmuka yang menggabungkan fungsionalitas dua antarmuka lainnya, yaitu System.IObservable<'T> dan IDelegateEvent. Oleh karena itu, Event memiliki fungsionalitas delegasi yang setara dalam bahasa pemrograman lain, ditambah fungsionalitas tambahan dari IObservable, yang berarti bahwa kejadian F# mendukung pemfilteran kejadian dan menggunakan fungsi kelas satu F# dan ekspresi lambda sebagai penanganan aktivitas. Fungsionalitas ini disediakan dalam modul Kejadian.

Untuk membuat kejadian pada kelas yang bertindak sama seperti kejadian .NET Framework lainnya, tambahkan ke kelas tersebut pengikatan let yang mendefinisikan Event sebagai bidang dalam sebuah kelas. Anda dapat menentukan jenis argumen kejadian yang diinginkan sebagai argumen jenis, atau biarkan kosong dan buat kompilator menyimpulkan jenis yang sesuai. Anda juga harus menentukan anggota kejadian yang mengekspos kejadian sebagai kejadian CLI. Anggota ini harus memiliki atribut CLIEvent. Anggota tersebut dideklarasikan seperti properti dan implementasinya hanyalah panggilan ke properti Terbitkan dari kejadian tersebut. Pengguna kelas Anda dapat menggunakan metode Add dari kejadian yang diterbitkan untuk menambahkan penangan. Argumen untuk metode Add dapat berupa ekspresi lambda. Anda dapat menggunakan properti Trigger acara untuk menaikkan kejadian, meneruskan argumen ke fungsi penangan. Contoh kode berikut menggambarkan hal ini. Dalam contoh ini, argumen jenis yang disimpulkan untuk kejadian adalah tuple, yang mewakili argumen untuk ekspresi 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

Outputnya sebagai berikut.

Event1 occurred! Object data: Hello World!

Fungsionalitas tambahan yang disediakan oleh modul Event digambarkan di sini. Contoh kode berikut menggambarkan penggunaan dasar Event.create untuk membuat sebuah kejadian dan metode pemicu, menambahkan dua penanganan aktivitas dalam formulir ekspresi lambda, kemudian memicu kejadian untuk menjalankan kedua ekspresi 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.")

Output dari kode sebelumnya adalah seperti berikut.

Event occurred.
Given a value: Event occurred.

Memproses Aliran Kejadian

Alih-alih hanya menambahkan penanganan aktivitas untuk kejadian dengan menggunakan fungsi Event.add, Anda dalam menggunakan fungsi di modul Event untuk memproses aliran kejadian dengan cara yang sangat disesuaikan. Untuk melakukan ini, Anda menggunakan pipa teruskan (|>) bersama dengan kejadian sebagai nilai pertama dalam serangkaian panggilan fungsi, dan fungsi modul Event sebagai panggilan fungsi berikutnya.

Contoh kode berikut menunjukkan cara untuk menyiapkan kejadian yang dipanggil penangan hanya dalam kondisi tertentu.

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) )

Modul yang dapat diamati berisi fungsi serupa yang beroperasi pada objek yang dapat diamati. Objek yang dapat diamati mirip dengan kejadian, tetapi hanya berlangganan secara aktif ke kejadian jika mereka sendiri dijadikan langganan.

Menerapkan Kejadian Antarmuka

Saat Anda mengembangkan komponen antarmuka pengguna, Anda sering memulai dengan membuat formulir baru atau kontrol baru yang mewarisi dari formulir atau kontrol yang ada. Kejadian sering didefinisikan pada antarmuka, dan, dalam hal ini, Anda harus mengimplementasikan antarmuka untuk mengimplementasikan kejadian. Antarmuka System.ComponentModel.INotifyPropertyChanged mendefinisikan kejadian System.ComponentModel.INotifyPropertyChanged.PropertyChanged tunggal. Kode berikut menggambarkan cara mengimplementasikan kejadian yang ditentukan oleh antarmuka yang diwariskan ini:

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)

Jika Anda ingin menghubungkan kejadian di konstruktor, kodenya akan sedikit lebih rumit karena hubungan kejadian harus berada di blok then di konstruktor tambahan, seperti dalam contoh berikut:

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)

Lihat juga