Eventos (F#)

Los eventos permiten asociar las llamadas de función a las acciones del usuario y son importantes para la programación de GUI.Los eventos también los pueden desencadenar las aplicaciones o el sistema operativo.

Controlar eventos

Cuando se utiliza una biblioteca de GUI como Windows Forms o Windows Presentation Foundation (WPF), gran parte del código de la aplicación se ejecuta en respuesta a los eventos predefinidos por la biblioteca.Estos eventos predefinidos son miembros de clases de GUI, como formularios y controles.Se puede agregar comportamiento personalizado a un evento preexistente, como un clic del botón, haciendo referencia al evento con nombre en cuestión (por ejemplo, el evento Click de la clase Form) e invocando el método Add, tal y como se muestra en el código siguiente.Si se ejecuta en F# Interactive, se omitirá la llamada a Run.

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)

El tipo del método Add es ('a -> unit) -> unit.Por consiguiente, el método de control de eventos toma un parámetro, que suele ser los argumentos del evento, y devuelve unit.En el ejemplo anterior se muestra el controlador de eventos como una expresión lambda.El controlador de eventos también puede ser un valor de función, como en el ejemplo de código siguiente.En el ejemplo de código siguiente también se muestra el uso de los parámetros de controlador de eventos, que proporcionan información específica del tipo de evento.Para un evento MouseMove, el sistema pasa un objeto MouseEventArgs, que contiene la posición X e Y del puntero.

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)

Crear eventos personalizados

Los eventos de F# están representados por la clase Event de F#, que implementa la interfaz IEvent.IEvent es una interfaz que combina la funcionalidad de otras dos interfaces, IObservable<T> e IDelegateEvent.Por consiguiente, la clase Event tiene una funcionalidad de delegados equivalente a la de otros lenguajes, además de la funcionalidad de IObservable, lo que significa que los eventos de F# admiten el filtrado así como el uso de expresiones lambda y funciones de primera clase de F# como controladores de eventos.Esta funcionalidad se proporciona en el módulo Event.

Para crear en una clase un evento que actúe como cualquier otro evento de .NET Framework, agregue a la clase un enlace let que defina un objeto Event como un campo de una clase.Puede especificar el tipo de argumento de evento que desee como tipo de argumento o puede dejarlo en blanco y dejar que el compilador infiera el tipo adecuando.Además, debe definir un miembro de evento que exponga el evento como un evento de CLI.Este miembro debe tener el atributo CLIEvent.Se declara como una propiedad y su implementación no es más que una llamada a la propiedad Publish del evento.Los usuarios de la clase pueden usar el método Add del evento publicado para agregar un controlador.El argumento del método Add puede ser una expresión lambda.Puede utilizar la propiedad de Trigger de evento para provocar el evento, pasando los argumentos a la función de controlador.En el siguiente ejemplo código se muestra cómo hacerlo.En este ejemplo, el argumento de tipo inferido para el evento es una tupla, que representa los argumentos de la expresión lambda.

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

La salida es la siguiente.

Event1 occurred! Object data: Hello World!

Aquí se muestra la funcionalidad adicional que proporciona el módulo Event.En el siguiente ejemplo de código, se muestra el uso básico de Event.create para crear un evento y un método desencadenador, agregar dos controladores de eventos en forma de expresiones lambda y, a continuación, desencadenar el evento para ejecutar ambas expresiones 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.")

La salida del código anterior es la siguiente.

Event occurred.
Given a value: Event occurred.

Procesar secuencias de eventos

En lugar de limitarse a agregar un controlador de un evento mediante la función Event.add, puede utilizar las funciones del módulo Event para procesar secuencias de eventos de maneras muy personalizadas.Para ello, se utiliza la canalización hacia delante (|>) junto con el evento como primer valor en una serie de llamadas de función, y las funciones del módulo Event como llamadas de función subsiguientes.

En el siguiente ejemplo de código, se muestra cómo configurar un evento cuyo controlador se invoca únicamente en determinadas condiciones.

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

El módulo Observable contiene funciones similares que se usan con objetos observables.Los objetos observables son similares a los eventos pero solo se suscriben activamente a los eventos si ellos mismos son objeto de una suscripción.

Implementar un evento de interfaz

Cuando desarrolle componentes de la interfaz de usuario, empiece a menudo creando un nuevo formulario o un nuevo control que herede de un formulario o control existente.Los eventos son con frecuencia definido en una interfaz, y, en ese caso, debe implementar la interfaz para implementar el evento.La interfaz de INotifyPropertyChanged define un único evento de PropertyChanged .El código siguiente muestra cómo implementar el evento ese esta interfaz heredada definido:

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)

Si desea enlazar el evento en el constructor, el código es algo más complicado porque el enlace del evento debe estar en then bloqueado en un constructor adicional, como en el ejemplo siguiente:

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)

Vea también

Referencia

Expresiones lambda: la palabra clave fun (F#)

Control.Event (Módulo de F#)

Control.Event<'T> (Clase de F#)

Control.Event<'Delegate,'Args> (Clase de F#)

Otros recursos

Miembros (F#)

Eventos y delegados