Condividi tramite


Avvenimenti

Gli eventi consentono di associare le chiamate di funzione alle azioni utente e sono importanti nella programmazione gui. Gli eventi possono essere attivati anche dalle applicazioni o dal sistema operativo.

Gestione degli eventi

Quando si usa una libreria GUI come Windows Form o Windows Presentation Foundation (WPF), gran parte del codice nell'applicazione viene eseguita in risposta a eventi predefiniti dalla libreria. Questi eventi predefiniti sono membri di classi GUI, ad esempio moduli e controlli. È possibile aggiungere un comportamento personalizzato a un evento preesistente, ad esempio un clic sul pulsante, facendo riferimento all'evento denominato specifico di interesse ( ad esempio, l'evento Click della Form classe ) e richiamando il Add metodo , come illustrato nel codice seguente. Se si esegue questa operazione da F# Interactive, omettere la chiamata a 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)

Il tipo del Add metodo è ('a -> unit) -> unit. Di conseguenza, il metodo del gestore eventi accetta un parametro, in genere gli argomenti dell'evento e restituisce unit. L'esempio precedente mostra il gestore eventi come espressione lambda. Il gestore eventi può anche essere un valore di funzione, come nell'esempio di codice seguente. Nell'esempio di codice seguente viene illustrato anche l'uso dei parametri del gestore eventi, che forniscono informazioni specifiche per il tipo di evento. Per un MouseMove evento, il sistema passa un System.Windows.Forms.MouseEventArgs oggetto , che contiene la X posizione e Y del puntatore.

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)

Creazione di eventi personalizzati

Gli eventi F# sono rappresentati dal tipo di evento F#, che implementa l'interfaccia IEvent . IEvent è un'interfaccia che combina la funzionalità di due altre interfacce System.IObservable<'T> e IDelegateEvent. Pertanto, Eventi delegati hanno la funzionalità equivalente di delegati in altri linguaggi, oltre alle funzionalità aggiuntive di IObservable, il che significa che gli eventi F# supportano il filtro degli eventi e l'uso di funzioni di prima classe F# e espressioni lambda come gestori eventi. Questa funzionalità viene fornita nel modulo Evento.

Per creare un evento in una classe che agisce esattamente come qualsiasi altro evento .NET Framework, aggiungere alla classe un'associazione let che definisce un oggetto Event come campo in una classe. È possibile specificare il tipo di argomento dell'evento desiderato come argomento di tipo oppure lasciarlo vuoto e fare in modo che il compilatore deduca il tipo appropriato. È inoltre necessario definire un membro dell'evento che espone l'evento come evento dell'interfaccia della riga di comando. Questo membro deve avere l'attributo CLIEvent . Viene dichiarato come una proprietà e la relativa implementazione è solo una chiamata alla proprietà Publish dell'evento. Gli utenti della classe possono usare il Add metodo dell'evento pubblicato per aggiungere un gestore. L'argomento per il Add metodo può essere un'espressione lambda. È possibile utilizzare la Trigger proprietà dell'evento per generare l'evento, passando gli argomenti alla funzione del gestore. Nell'esempio di codice seguente viene illustrato questo. In questo esempio, l'argomento di tipo dedotto per l'evento è una tupla, che rappresenta gli argomenti per l'espressione 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

L'output è indicato di seguito.

Event1 occurred! Object data: Hello World!

Le funzionalità aggiuntive fornite dal Event modulo sono illustrate qui. Nell'esempio di codice seguente viene illustrato l'uso di base di Event.create per creare un evento e un metodo trigger, aggiungere due gestori eventi sotto forma di espressioni lambda e quindi attivare l'evento per eseguire entrambe le espressioni 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.")

L'output del codice precedente è il seguente.

Event occurred.
Given a value: Event occurred.

Elaborazione di flussi di eventi

Anziché aggiungere semplicemente un gestore eventi per un evento usando la funzione Event.add , è possibile usare le funzioni nel Event modulo per elaborare flussi di eventi in modi altamente personalizzati. A tale scopo, usare la pipe di inoltro (|>) insieme all'evento come primo valore in una serie di chiamate di funzione e le funzioni del Event modulo come chiamate di funzione successive.

Nell'esempio di codice seguente viene illustrato come configurare un evento per il quale il gestore viene chiamato solo in determinate condizioni.

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

Il modulo Observable contiene funzioni simili che operano su oggetti osservabili. Gli oggetti osservabili sono simili agli eventi, ma sottoscrivono attivamente gli eventi solo se vengono sottoscritti.

Implementazione di un evento di interfaccia

Durante lo sviluppo di componenti dell'interfaccia utente, spesso si inizia creando un nuovo modulo o un nuovo controllo che eredita da una maschera o da un controllo esistente. Gli eventi vengono spesso definiti in un'interfaccia e, in tal caso, è necessario implementare l'interfaccia per implementare l'evento. L'interfaccia System.ComponentModel.INotifyPropertyChanged definisce un singolo System.ComponentModel.INotifyPropertyChanged.PropertyChanged evento. Il codice seguente illustra come implementare l'evento definito da questa interfaccia ereditata:

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)

Se si vuole associare l'evento nel costruttore, il codice è un po' più complicato perché l'hookup dell'evento deve trovarsi in un blocco in un then costruttore aggiuntivo, come nell'esempio seguente:

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)

Vedere anche