Freigeben über


Ereignisse (F#)

Mit Ereignissen ordnen Sie Benutzeraktionen Funktionsaufrufe zu. Sie sind wichtig in der GUI-Programmierung.Ereignisse können auch von den Anwendungen oder dem Betriebssystem ausgelöst werden.

Behandeln von Ereignissen

Wenn Sie eine GUI-Bibliothek, z. B. Windows Forms oder Windows Presentation Foundation (WPF), verwenden, wird ein großer Anteil des Codes in der Anwendung aufgrund von Ereignissen ausgeführt, die durch die Bibliothek vordefiniert sind.Diese vordefinierten Ereignisse sind Member von GUI-Klassen, z. B. Forms und Control.Sie können einem bereits vorhandenen Ereignis benutzerdefiniertes Verhalten, z. B. das Klicken auf eine Schaltfläche, hinzufügen, indem Sie auf das entsprechende Ereignis (z. B. das Click-Ereignis der Form-Klasse) verweisen und die Add-Methode aufrufen, wie im folgenden Code gezeigt.Lassen Sie bei der Ausführung in F# Interactive den Aufruf von Run weg.

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)

Der Typ der Add-Methode ist ('a -> unit) -> unit.Daher akzeptiert die Ereignishandlermethode einen Parameter, in der Regel die Ereignisargumente, und gibt unit zurück.Im vorherigen Beispiel ist der Ereignishandler ein Lambda-Ausdruck.Der Ereignishandler kann auch ein Funktionswert sein, wie im folgenden Codebeispiel.Im folgenden Codebeispiel wird auch die Verwendung der Ereignishandlerparameter veranschaulicht, die spezifische Informationen für den Typ des Ereignisses bereitstellen.Für ein MouseMove-Ereignis übergibt das System ein MouseEventArgs-Objekt, das die X- und Y-Position des Zeigers enthält.

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)

Erstellen von benutzerdefinierten Ereignissen

F#-Ereignisse werden durch die F# Event-Klasse dargestellt, die die IEvent-Schnittstelle implementiert.IEvent ist eine Schnittstelle, die die Funktionalität zweier anderer Schnittstellen, IObservable<T> und IDelegateEvent, kombiniert.Daher verfügen Events über die gleiche Funktionalität wie Delegaten in anderen Sprachen und zusätzlich über die Funktionen von IObservable. F#-Ereignisse unterstützen somit die Ereignisfilterung und das Verwenden von F#-Funktionen der ersten Klasse und von Lambda-Ausdrücken als Ereignishandler.Diese Funktionalität wird im Event-Modul bereitgestellt.

Zum Erstellen eines Ereignisses für eine Klasse, das sich wie ein beliebiges anderes .NET Framework-Ereignis verhält, fügen Sie der Klasse eine let-Bindung hinzu, die ein Event als Feld in einer Klasse definiert.Sie können den gewünschten Ereignisargumenttyp als Typargument angeben oder den Wert freilassen und einen geeigneten Wert durch den Compiler bestimmen lassen.Sie müssen außerdem einen Ereignismember definieren, der das Ereignis als CLI-Ereignis verfügbar macht.Dieser Member sollte über das CLIEvent-Attribut verfügen.Er wird ähnlich wie eine Eigenschaft deklariert, und seine Implementierung ist nur ein Aufruf der Eigenschaft zum Veröffentlichen des Ereignisses.Benutzer der Klasse können die Add-Methode des veröffentlichten Ereignisses verwenden, um einen Handler hinzuzufügen.Das Argument für die Add-Methode kann ein Lambda-Ausdruck sein.Sie können die Trigger-Eigenschaft des Ereignisses verwenden, um das Ereignis auszulösen und die Argumente der Handlerfunktion übergeben.Dies wird im folgenden Codebeispiel veranschaulicht.In diesem Beispiel ist das abgeleitete Typargument für das Ereignis ein Tupel, das die Argumente für den Lambda-Ausdruck darstellt.

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

Die Ausgabe lautet wie folgt.

Event1 occurred! Object data: Hello World!

Die zusätzliche vom Event-Modul bereitgestellte Funktionalität wird hier veranschaulicht.Im folgenden Codebeispiel wird die grundlegende Verwendung von Event.create zur Erstellung eines Ereignisses und einer Triggermethode, zum Hinzufügen zweier Ereignishandler in der Form von Lambda-Ausdrücken und dem Auslösen des Ereignisses zur Ausführung beider Lambda-Ausdrücke dargestellt.

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

Der obige Code gibt Folgendes aus.

Event occurred.
Given a value: Event occurred.

Verarbeiten von Ereignisstreams

Statt einfach mit der Event.add-Funktion einen Ereignishandler für ein Ereignis hinzuzufügen, können Sie die Funktionen im Event-Modul verwenden, um Ereignisstreams hochgradig benutzerdefiniert zu verarbeiten.Hierzu verwenden Sie den Vorwärtspipeoperator (|>) zusammen mit dem Ereignis als ersten Wert in einer Reihe von Funktionsaufrufen und die Event-Modulfunktionen als nachfolgende Funktionsaufrufe.

Im folgenden Codebeispiel wird die Einrichtung eines Ereignisses gezeigt, für das der Handler nur unter bestimmten Bedingungen aufgerufen wird.

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

Das Observable-Modul enthält ähnliche Funktionen, die auf Observable-Objekte wirken.Observable-Objekte sind Ereignissen ähnlich, abonnieren Ereignisse aber nur, wenn sie selbst abonniert werden.

Ein Schnittstellen-Ereignis implementieren

Wenn Sie Benutzeroberfläche-Komponenten entwickeln, beginnen Sie häufig, indem Sie ein neues Formular oder ein neues Steuerelement erstellen, das aus einem vorhandenen Formular oder ein Steuerelement erbt.Ereignisse werden häufig auf einer Schnittstelle definiert, und in diesem Fall die Schnittstelle implementieren muss, um das Ereignis zu implementieren.Die INotifyPropertyChanged-Schnittstelle definiert ein einzelnes PropertyChanged-Ereignis.Im folgenden Code wird veranschaulicht, wie das Ereignis implementiert, das diese geerbte Schnittstelle definierte:

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)

Wenn Sie zum Einbinden das Ereignis im Konstruktor soll, ist der Code, da der Art anschluß Ereignis in einem then-Block in einem zusätzlichen Konstruktor sein muss, wie im folgenden Beispiel schwieriger:

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)

Siehe auch

Referenz

Lambda-Ausdrücke: Das fun-Schlüsselwort (F#)

Control.Event-Modul (F#)

Control.Event<'T>-Klasse (F#)

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

Weitere Ressourcen

Member (F#)

Ereignisse und Delegaten