Поделиться через


События (F#)

События позволяют связывать вызовы функций с действиями пользователя и являются важным элементом в программировании графического интерфейса пользователя.События могут также инициироваться приложениями или операционной системой.

Обработка событий

При использовании библиотеки графического интерфейса пользователя, такой как Windows Forms или Windows Presentation Foundation (WPF), значительная часть кода в приложении выполняется в ответ на события, предопределенные в библиотеке.Эти предопределенные события являются членами классов графического интерфейса пользователя, таких как формы и элементы управления.Можно добавить произвольное поведение к уже существующему событию, такому как нажатие кнопки, сославшись на интересующее именованное событие (например, событие Click класса Form) и вызвав метод Add, как показано в следующем коде.При запуске этого кода из F# Interactive вызов метода 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)

Тип метода Add — ('a -> unit) -> unit.Следовательно, метод, обрабатывающий событие, принимает один параметр — обычно аргументы события — и возвращает значение типа unit.В предыдущем примере обработчик события показан как лямбда-выражение.Обработчик события также может представлять собой значение функции, как в следующем примере кода.В следующем примере кода также показано использование параметров обработчика события, содержащих данные, зависящие от типа события.Для события MouseMove система передает объект MouseEventArgs, содержащий координаты X и 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)

Создание пользовательских событий

События в языке F# представлены классом Event языка F#, реализующим интерфейс IEvent.IEvent представляет собой интерфейс, объединяющий функциональные возможности двух других интерфейсов, IObservable<T> и IDelegateEvent.Следовательно, события Event обладают функциональными возможностями, эквивалентными возможностям делегатов в других языках, и дополнительно функциональными возможностями интерфейса IObservable; это означает, что события F# поддерживают фильтрацию событий и использование функций первого класса и лямбда-выражений языка F# в качестве обработчиков событий.Эти функциональные возможности обеспечиваются модулем Event.

Чтобы создать для класса событие, которое ведет себя точно так же, как любое другое событие платформы .NET Framework, добавьте в класс привязку let, определяющую событие Event как поле в классе.В качестве аргумента типа можно указать требуемый тип аргумента события или оставить его пустым, чтобы соответствующий тип был выведен компилятором.Необходимо также определить член события, предоставляющего это событие как событие CLI.Этот член должен иметь атрибут CLIEvent.Он объявляется как свойство, и его реализация представляет собой просто вызов свойства Publish события.Пользователи класса могут использовать метод Add опубликованного события для добавления обработчика.Аргумент метода Add может быть лямбда-выражением.Можно использовать свойство Trigger события, чтобы вызвать событие, при передаче аргументов функции обработчика.Это показано в следующем примере кода.В этом примере выведенный аргумент типа для события — кортеж, представляющий аргументы для лямбда-выражения.

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

Выходные данные выглядят следующим образом.

Event1 occurred! Object data: Hello World!

Здесь иллюстрируется дополнительная функциональная возможность, обеспечиваемая модулем Event.Следующий пример кода иллюстрирует основное использование функции Event.create для создания события и метода-триггера, добавления двух обработчиков события в виде лямбда-выражений и последующего инициирования события для выполнения обоих лямбда-выражений.

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

Результат выполнения приведенного кода будет следующим.

Event occurred.
Given a value: Event occurred.

Обработка потоков событий

Вместо простого добавления обработчика события для события с помощью функции Event.add можно использовать функции в модуле Event для обработки потоков событий настраиваемыми способами.Это делается путем использования оператора прямого конвейера (|>) вместе с событием в качестве первого значения в серии вызовов функций и функций модуля Event в качестве последующих вызовов функций.

В следующем примере кода демонстрируется, как настроить событие, обработчик которого вызывается только при определенных условиях.

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

Модуль Observable содержит аналогичные функции, действующие на наблюдаемые объекты.Наблюдаемые объекты аналогичны событиям, но они активно подписываются на события только при создании подписки на такой объект.

Реализация событий интерфейса

При разработке компонентов пользовательского интерфейса, часто начать с создания новой формы или создать элемент управления, наследуемый от существующих формы или элемента управления.События часто определяются в интерфейсе, и в этом случае необходимо реализовать интерфейс для реализации событие.Интерфейс INotifyPropertyChanged указывающий событие PropertyChanged.Следующий код показывает, как реализовать событие, этот интерфейс, наследуемый:

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)

Если необходимо подключить событие в конструкторе, то код бит более осложнять поскольку hookup события должен находиться в блоке then в дополнительном конструкторе, как в следующем примере:

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)

См. также

Ссылки

Лямбда-выражения: ключевое слово fun (F#)

Модуль Control.Event (F#)

Класс Control.Event<'T> (F#)

Класс Control.Event<'Delegate,'Args> (F#)

Другие ресурсы

Члены (F#)

События и делегаты