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


События

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

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

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

Тип Add метода .('a -> unit) -> unit Поэтому метод обработчика событий принимает один параметр, как правило, аргументы событий и возвращаются unit. В предыдущем примере показан обработчик событий как лямбда-выражение. Обработчик событий также может быть значением функции, как показано в следующем примере кода. В следующем примере кода также показано использование параметров обработчика событий, которые предоставляют сведения, относящиеся к типу события. MouseMove Для события система передает System.Windows.Forms.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# представлены типом события F#, который реализует интерфейс IEvent . IEvent — это интерфейс, System.IObservable<'T> который объединяет функциональные возможности двух других интерфейсов и IDelegateEvent. Таким образом, в s есть эквивалентные функциональные возможности делегатов на других языках, а также дополнительные функции изIObservable, что означает, Eventчто события F# поддерживают фильтрацию событий и использование функций первого класса F# и лямбда-выражений в качестве обработчиков событий. Эта функция предоставляется в модуле событий.

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

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

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

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

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

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

При разработке компонентов пользовательского интерфейса часто создается новая форма или новый элемент управления, наследуемый от существующей формы или элемента управления. События часто определяются в интерфейсе, и в этом случае необходимо реализовать интерфейс для реализации события. Интерфейс System.ComponentModel.INotifyPropertyChanged определяет одно System.ComponentModel.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 {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)

Если вы хотите подключить событие в конструкторе, код немного сложнее, так как перехват событий должен находиться в 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 {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)

См. также