Udostępnij za pośrednictwem


Zdarzenia (F#)

Zdarzenia umożliwiają kojarzenie wywołań funkcji z akcjami użytkownika i są ważne w programowaniu graficznych interfejsów użytkownika.Zdarzenia mogą być też wywoływane przez aplikacje lub system operacyjny.

Obsługa zdarzeń

Gdy jest używana biblioteka graficznych interfejsów użytkownika, taka jak Windows Forms lub Windows Presentation Foundation (WPF), większość kodu aplikacji działa w odpowiedzi na zdarzenia, które są wstępnie zdefiniowane w tej bibliotece.Te wstępnie zdefiniowane zdarzenia są elementami członkowskimi klas graficznego interfejsu użytkownika, takich jak formularze i formanty.Do już istniejącego zdarzenia, takiego jak kliknięcie przycisku, można dodać zachowanie niestandardowe, odwołując się do określonego nazwanego zdarzenia (na przykład zdarzenia Click klasy Form) i wywołując metodę Add, tak jak pokazano w poniższym kodzie.W przypadku uruchamiania tego kodu w interakcyjnej wersji języka F# należy pominąć wywołanie metody 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)

Typ metody Add to ('a -> unit) -> unit.Dlatego metoda obsługi zdarzeń przyjmuje jeden parametr, zazwyczaj argumenty zdarzenia, i zwraca typ unit.W poprzednim przykładzie pokazano program obsługi zdarzeń w postaci wyrażenia lambda.Program obsługi zdarzeń może być także wartością funkcji, tak jak w poniższym przykładzie kodu.W poniższym przykładzie kodu pokazano także użycie parametrów programu obsługi zdarzeń, które dostarczają informacje charakterystyczne dla typu zdarzenia.Dla zdarzenia MouseMove system przekazuje obiekt MouseEventArgs, który zawiera pozycje X i Y wskaźnika.

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)

Tworzenie zdarzeń niestandardowych

Zdarzenia języka F# są reprezentowane przez klasę Event języka F#, która implementuje interfejs IEvent.Interfejs IEvent jest interfejsem łączącym funkcje dwóch innych interfejsów — IObservable i IDelegateEvent.Dlatego klasa Event ma własną funkcjonalność delegatów w innych językach oraz dodatkową funkcjonalność z interfejsu IObservable, co oznacza, że zdarzenia języka F# obsługują filtrowanie zdarzeń i używanie najważniejszych funkcji języka F# oraz wyrażeń lambda jako programów obsługi zdarzeń.Ta funkcjonalność jest dostarczana w module Event.

Aby utworzyć zdarzenie w klasie, które będzie działać tak jak dowolne inne zdarzenie programu .NET Framework, dodaj do klasy powiązanie let definiujące obiekt Event jako pole w klasie.Odpowiedni typ argumentu zdarzenia można określić jako argument typu, ale można też pozostawić ten typu pusty, co spowoduje, że kompilator wywnioskuje odpowiedni typ.Należy także zdefiniować element członkowski zdarzenia, który będzie uwidaczniał zdarzenie jako zdarzenie CLI.Ten element członkowski musi mieć atrybut CLIEvent.Jest on deklarowany tak jak właściwość, a jego implementacja to po prostu wywołanie właściwości Publish zdarzenia.Użytkownicy klasy mogą używać metody Add opublikowanego zdarzenia w celu dodawania programu obsługi.Argumentem metody Add może być wyrażenie lambda.Aby wygenerować zdarzenie, przekazując argumenty do funkcji programu obsługi, można użyć właściwości Trigger zdarzenia.Pokazano to w poniższym przykładzie kodu.W tym przykładzie wywnioskowany argument typu dla zdarzenia to spójna kolekcja, która reprezentuje argumenty wyrażenia 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

Dane wyjściowe są następujące:

Event1 occurred! Object data: Hello World!

Dodatkową funkcjonalność modułu Event pokazano tutaj.W poniższym przykładzie kodu pokazano podstawowe zastosowanie metody Event.create w celu utworzenia zdarzenia i metody wyzwalacza, dodanie dwóch programów obsługi zdarzeń w formie wyrażeń lambda oraz wyzwolenie zdarzenia w celu wykonania obu wyrażeń 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.")

Dane wyjściowe poprzedniego kodu wyglądają następująco:

Event occurred.
Given a value: Event occurred.

Przetwarzanie strumieni zdarzeń

Zamiast po prostu dodać program obsługi zdarzeń dla zdarzenia, używając funkcji Event.add, można użyć funkcji dostępnych w module Event, aby w wysoce dostosowany sposób przetwarzać strumienie zdarzeń.W tym celu należy użyć potoku przesyłania dalej (|>) wraz ze zdarzeniem jako pierwszą wartością w serii wywołań funkcji oraz funkcji modułu Event jako kolejnych wywołań funkcji.

W poniższym przykładzie kodu pokazano, jak skonfigurować zdarzenie, dla którego program obsługi jest wywoływany tylko w określonych warunkach.

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

Widoczny moduł zawiera podobne funkcje, które wykonują operacje na widocznych obiektach.Widoczne obiekty są podobne do zdarzeń, ale aktywnie subskrybują zdarzenia tylko wtedy, gdy same są subskrybowane.

Implementowanie zdarzenia interfejsu

Projektowanie składników interfejsu użytkownika często rozpoczyna się od utworzenia nowego formularza lub nowego formantu, który dziedziczy z istniejącego formularza lub formantu.Zdarzenia są często definiowane w interfejsie, a w takim przypadku trzeba zaimplementować interfejs służący do implementacji zdarzeń.Interfejs INotifyPropertyChanged definiuje pojedyncze zdarzenie PropertyChanged.Poniższy kod ilustruje sposób implementacji zdarzenia definiowanego przez ten dziedziczony interfejs:

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)

Aby zaczepić zdarzenie w konstruktorze, trzeba użyć nieco bardziej skomplikowanego kodu, ponieważ zaczep zdarzenia musi znajdować się w bloku then w dodatkowym konstruktorze, tak jak pokazano w poniższym przykładzie:

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)

Zobacz też

Informacje

Wyrażenia lambda: fun — Słowo kluczowe (F#)

Control.Event — Moduł (F#)

Control.Event<'T> — Klasa (F#)

Control.Event<'Delegate,'Args> — Klasa (F#)

Inne zasoby

Członkowie (F#)

Obsługa i wywoływanie zdarzeń