Freigeben über


Ereignisse

Mit Ereignissen können Sie Funktionsaufrufe mit Benutzeraktionen verknüpfen und sind bei der GUI-Programmierung wichtig. Ereignisse können auch von Ihren Anwendungen oder vom Betriebssystem ausgelöst werden.

Behandeln von Ereignissen

Wenn Sie eine GUI-Bibliothek wie Windows Forms oder Windows Presentation Foundation (WPF) verwenden, wird ein Großteil des Codes in Ihrer Anwendung als Reaktion auf Ereignisse ausgeführt, die von der Bibliothek vordefinierte sind. Diese vordefinierten Ereignisse sind Elemente von GUI-Klassen wie Formularen und Steuerelementen. Sie können einem bereits vorhandenen Ereignis, z. B. einem Schaltflächenklick, benutzerdefiniertes Verhalten hinzufügen, indem Sie auf das spezifische benannte Ereignis von Interesse (z. B. das Click Ereignis der Form Klasse) verweisen und die Add Methode aufrufen, wie im folgenden Code gezeigt. Wenn Sie dies von F# Interactive ausführen, lassen Sie den Aufruf von 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)

Der Typ der Add Methode lautet ('a -> unit) -> unit. Daher verwendet die Ereignishandlermethode einen Parameter, in der Regel die Ereignisargumente und gibt zurück unit. Das vorherige Beispiel zeigt den Ereignishandler als Lambda-Ausdruck. Der Ereignishandler kann auch ein Funktionswert sein, wie im folgenden Codebeispiel gezeigt. Das folgende Codebeispiel zeigt auch die Verwendung der Ereignishandlerparameter, die spezifische Informationen für den Ereignistyp bereitstellen. Bei einem MouseMove Ereignis übergibt das System ein System.Windows.Forms.MouseEventArgs Objekt, das die Position und Y die X 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 den F# -Ereignistyp dargestellt, der die IEvent-Schnittstelle implementiert. IEvent ist selbst eine Schnittstelle, die die Funktionalität zweier anderer Schnittstellen System.IObservable<'T> und IDelegateEvent kombiniert. EventDaher verfügen s über die äquivalente Funktionalität von Delegaten in anderen Sprachen sowie über die zusätzliche Funktionalität IObservablevon , was bedeutet, dass F#-Ereignisse die Ereignisfilterung und die Verwendung von F#-Funktionen und Lambda-Ausdrücken als Ereignishandler unterstützen. Diese Funktionalität wird im Ereignismodul bereitgestellt.

Um ein Ereignis für eine Klasse zu erstellen, die wie jedes andere .NET Framework-Ereignis fungiert, fügen Sie der Klasse eine let Bindung hinzu, die ein Event Feld in einer Klasse definiert. Sie können den gewünschten Ereignisargumenttyp als Typargument angeben oder leer lassen und den Compiler den entsprechenden Typ ableiten lassen. Sie müssen auch ein Ereignismitglied definieren, das das Ereignis als CLI-Ereignis verfügbar macht. Dieses Element sollte über das CLIEvent-Attribut verfügen. Sie wird wie eine Eigenschaft deklariert, und die Implementierung ist nur ein Aufruf der Publish-Eigenschaft des Ereignisses. Benutzer Ihrer 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. Mit der Trigger Eigenschaft des Ereignisses können Sie das Ereignis auslösen und die Argumente an die Handlerfunktion übergeben. Im folgenden Codebeispiel wird dies 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<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

Die Ausgabe lautet wie folgt.

Event1 occurred! Object data: Hello World!

Die zusätzliche Funktionalität, die vom Event Modul bereitgestellt wird, wird hier veranschaulicht. Im folgenden Codebeispiel wird die grundlegende Verwendung zum Event.create Erstellen eines Ereignisses und einer Triggermethode veranschaulicht, zwei Ereignishandler in Form von Lambda-Ausdrücken hinzugefügt und dann das Ereignis ausgelöst, um beide Lambda-Ausdrücke auszuführen.

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

Die Ausgabe des vorherigen Codes lautet wie folgt.

Event occurred.
Given a value: Event occurred.

Verarbeiten von Ereignisdatenströmen

Anstatt nur einen Ereignishandler für ein Ereignis mithilfe der Event.add-Funktion hinzuzufügen, können Sie die Funktionen im Event Modul verwenden, um Datenströme von Ereignissen auf hochgradig angepasste Weise zu verarbeiten. Dazu verwenden Sie die Weiterleitungspipeline (|>) zusammen mit dem Ereignis als ersten Wert in einer Reihe von Funktionsaufrufen und die Event Modulfunktionen als nachfolgende Funktionsaufrufe.

Das folgende Codebeispiel zeigt, wie Sie ein Ereignis einrichten, 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 observierbaren Objekten arbeiten. Observable-Objekte sind ähnlich wie Ereignisse, aber nur aktiv abonnieren, wenn sie selbst abonniert werden.

Implementieren eines Schnittstellenereignisses

Beim Entwickeln von UI-Komponenten beginnen Sie häufig mit dem Erstellen eines neuen Formulars oder eines neuen Steuerelements, das von einem vorhandenen Formular oder Steuerelement erbt. Ereignisse werden häufig auf einer Schnittstelle definiert, und in diesem Fall müssen Sie die Schnittstelle implementieren, um das Ereignis zu implementieren. Die System.ComponentModel.INotifyPropertyChanged Schnittstelle definiert ein einzelnes System.ComponentModel.INotifyPropertyChanged.PropertyChanged Ereignis. Der folgende Code veranschaulicht, wie das Ereignis implementiert wird, das von dieser geerbten Schnittstelle definiert wurde:

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)

Wenn Sie das Ereignis im Konstruktor verbinden möchten, ist der Code etwas komplizierter, da sich der Ereignisanschluss in einem Block in einem then zusätzlichen Konstruktor befinden muss, wie im folgenden Beispiel:

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)

Siehe auch