Händelser
Med händelser kan du associera funktionsanrop med användaråtgärder och är viktiga i GUI-programmering. Händelser kan också utlösas av dina program eller av operativsystemet.
Hantera händelser
När du använder ett GUI-bibliotek som Windows Forms eller Windows Presentation Foundation (WPF) körs mycket av koden i programmet som svar på händelser som är fördefinierade av biblioteket. Dessa fördefinierade händelser är medlemmar i GUI-klasser som formulär och kontroller. Du kan lägga till anpassat beteende i en befintlig händelse, till exempel ett knappklick, genom att referera till den specifika namngivna händelsen av intresse (till exempel Click
händelsen Form
för klassen) och anropa Add
metoden, som du ser i följande kod. Om du kör detta från F# Interactive utelämnar du anropet till 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)
Metodens Add
typ är ('a -> unit) -> unit
. Därför tar händelsehanterarmetoden en parameter, vanligtvis händelseargumenten, och returnerar unit
. I föregående exempel visas händelsehanteraren som ett lambda-uttryck. Händelsehanteraren kan också vara ett funktionsvärde, som i följande kodexempel. Följande kodexempel visar också användningen av händelsehanterarparametrarna, som ger information som är specifik för typen av händelse. För en MouseMove
händelse skickar systemet ett System.Windows.Forms.MouseEventArgs
objekt som innehåller pekarens X
position och 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)
Skapa anpassade händelser
F#-händelser representeras av händelsetypen F# som implementerar IEvent-gränssnittet. IEvent
är i sig ett gränssnitt som kombinerar funktionerna i två andra gränssnitt och System.IObservable<'T>
IDelegateEvent. Event
Därför har s motsvarande funktioner för ombud på andra språk, plus ytterligare funktioner från IObservable
, vilket innebär att F#-händelser stöder händelsefiltrering och använder F#-förstklassiga funktioner och lambda-uttryck som händelsehanterare. Den här funktionen finns i modulen Händelse.
Om du vill skapa en händelse i en klass som fungerar precis som andra .NET Framework-händelser lägger du till en let
bindning i klassen som definierar ett Event
som ett fält i en klass. Du kan ange önskad typ av händelseargument som typargument, eller lämna den tom och låta kompilatorn härleda lämplig typ. Du måste också definiera en händelsemedlem som exponerar händelsen som en CLI-händelse. Den här medlemmen bör ha CLIEvent-attributet. Den deklareras som en egenskap och dess implementering är bara ett anrop till händelsens publiceringsegenskap . Användare av klassen kan använda Add
metoden för den publicerade händelsen för att lägga till en hanterare. Argumentet för Add
metoden kan vara ett lambda-uttryck. Du kan använda Trigger
egenskapen för händelsen för att skapa händelsen och skicka argumenten till hanteringsfunktionen. Följande kodexempel illustrerar detta. I det här exemplet är argumentet för den här typen för händelsen en tuppeln som representerar argumenten för lambda-uttrycket.
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
Utdata är följande.
Event1 occurred! Object data: Hello World!
De ytterligare funktioner som tillhandahålls av modulen Event
visas här. Följande kodexempel illustrerar den grundläggande användningen av Event.create
för att skapa en händelse och en utlösarmetod, lägger till två händelsehanterare i form av lambda-uttryck och utlöser sedan händelsen för att köra båda lambda-uttrycken.
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.")
Utdata från föregående kod är följande.
Event occurred.
Given a value: Event occurred.
Bearbeta händelse Flöden
I stället för att bara lägga till en händelsehanterare för en händelse med hjälp av funktionen Event.add kan du använda funktionerna i modulen Event
för att bearbeta händelseströmmar på mycket anpassade sätt. För att göra detta använder du framåtröret (|>
) tillsammans med händelsen som det första värdet i en serie funktionsanrop och modulfunktionerna Event
som efterföljande funktionsanrop.
I följande kodexempel visas hur du konfigurerar en händelse som hanteraren bara anropas för under vissa förhållanden.
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) )
Den observerbara modulen innehåller liknande funktioner som fungerar på observerbara objekt. Observerbara objekt liknar händelser men prenumererar bara aktivt på händelser om de själva prenumererar på.
Implementera en gränssnittshändelse
När du utvecklar gränssnittskomponenter börjar du ofta med att skapa ett nytt formulär eller en ny kontroll som ärver från ett befintligt formulär eller en kontroll. Händelser definieras ofta i ett gränssnitt, och i så fall måste du implementera gränssnittet för att implementera händelsen. Gränssnittet System.ComponentModel.INotifyPropertyChanged
definierar en enskild System.ComponentModel.INotifyPropertyChanged.PropertyChanged
händelse. Följande kod visar hur du implementerar händelsen som det här ärvda gränssnittet har definierat:
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)
Om du vill ansluta händelsen i konstruktorn är koden lite mer komplicerad eftersom händelsekopplingen måste finnas i ett then
block i en ytterligare konstruktor, som i följande exempel:
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)