Händelser

Med händelser kan du associera funktionsanrop med användaråtgärder och är viktiga i GUI-programmering. Händelser kan också utlösas av dina program eller av operativsystemet.

Hantera händelser

När du använder ett GUI-bibliotek som Windows Forms eller Windows Presentation Foundation (WPF) körs mycket av koden i programmet som svar på händelser som är fördefinierade av biblioteket. Dessa fördefinierade händelser är medlemmar i GUI-klasser som formulär och kontroller. Du kan lägga till anpassat beteende i en befintlig händelse, till exempel ett knappklick, genom att referera till den specifika namngivna händelsen av intresse (till exempel Click händelsen Form för klassen) och anropa Add metoden, som du ser i följande kod. Om du kör detta från F# Interactive utelämnar du anropet till 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)

Metodens Add typ är ('a -> unit) -> unit. Därför tar händelsehanterarmetoden en parameter, vanligtvis händelseargumenten, och returnerar unit. I föregående exempel visas händelsehanteraren som ett lambda-uttryck. Händelsehanteraren kan också vara ett funktionsvärde, som i följande kodexempel. Följande kodexempel visar också användningen av händelsehanterarparametrarna, som ger information som är specifik för typen av händelse. För en MouseMove händelse skickar systemet ett System.Windows.Forms.MouseEventArgs objekt som innehåller pekarens X position och 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)

Skapa anpassade händelser

F#-händelser representeras av händelsetypen F# som implementerar IEvent-gränssnittet. IEvent är i sig ett gränssnitt som kombinerar funktionerna i två andra gränssnitt och System.IObservable<'T>IDelegateEvent. EventDärför har s motsvarande funktioner för ombud på andra språk, plus ytterligare funktioner från IObservable, vilket innebär att F#-händelser stöder händelsefiltrering och använder F#-förstklassiga funktioner och lambda-uttryck som händelsehanterare. Den här funktionen finns i modulen Händelse.

Om du vill skapa en händelse i en klass som fungerar precis som andra .NET Framework-händelser lägger du till en let bindning i klassen som definierar ett Event som ett fält i en klass. Du kan ange önskad typ av händelseargument som typargument, eller lämna den tom och låta kompilatorn härleda lämplig typ. Du måste också definiera en händelsemedlem som exponerar händelsen som en CLI-händelse. Den här medlemmen bör ha CLIEvent-attributet. Den deklareras som en egenskap och dess implementering är bara ett anrop till händelsens publiceringsegenskap . Användare av klassen kan använda Add metoden för den publicerade händelsen för att lägga till en hanterare. Argumentet för Add metoden kan vara ett lambda-uttryck. Du kan använda Trigger egenskapen för händelsen för att skapa händelsen och skicka argumenten till hanteringsfunktionen. Följande kodexempel illustrerar detta. I det här exemplet är argumentet för den här typen för händelsen en tuppeln som representerar argumenten för lambda-uttrycket.

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

Utdata är följande.

Event1 occurred! Object data: Hello World!

De ytterligare funktioner som tillhandahålls av modulen Event visas här. Följande kodexempel illustrerar den grundläggande användningen av Event.create för att skapa en händelse och en utlösarmetod, lägger till två händelsehanterare i form av lambda-uttryck och utlöser sedan händelsen för att köra båda lambda-uttrycken.

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

Utdata från föregående kod är följande.

Event occurred.
Given a value: Event occurred.

Bearbeta händelse Flöden

I stället för att bara lägga till en händelsehanterare för en händelse med hjälp av funktionen Event.add kan du använda funktionerna i modulen Event för att bearbeta händelseströmmar på mycket anpassade sätt. För att göra detta använder du framåtröret (|>) tillsammans med händelsen som det första värdet i en serie funktionsanrop och modulfunktionerna Event som efterföljande funktionsanrop.

I följande kodexempel visas hur du konfigurerar en händelse som hanteraren bara anropas för under vissa förhållanden.

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

Den observerbara modulen innehåller liknande funktioner som fungerar på observerbara objekt. Observerbara objekt liknar händelser men prenumererar bara aktivt på händelser om de själva prenumererar på.

Implementera en gränssnittshändelse

När du utvecklar gränssnittskomponenter börjar du ofta med att skapa ett nytt formulär eller en ny kontroll som ärver från ett befintligt formulär eller en kontroll. Händelser definieras ofta i ett gränssnitt, och i så fall måste du implementera gränssnittet för att implementera händelsen. Gränssnittet System.ComponentModel.INotifyPropertyChanged definierar en enskild System.ComponentModel.INotifyPropertyChanged.PropertyChanged händelse. Följande kod visar hur du implementerar händelsen som det här ärvda gränssnittet har definierat:

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)

Om du vill ansluta händelsen i konstruktorn är koden lite mer komplicerad eftersom händelsekopplingen måste finnas i ett then block i en ytterligare konstruktor, som i följande exempel:

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)

Se även