次の方法で共有


イベント

イベントを使用すると、関数呼び出しをユーザー アクションに関連付けることができます。これは GUI プログラミングで重要です。 イベントは、アプリケーションまたはオペレーティング システムによってトリガーすることもできます。

イベントの処理

Windows フォームや Windows Presentation Foundation (WPF) などの GUI ライブラリを使用する場合、アプリケーション内のコードの多くは、ライブラリによって定義済みのイベントに応答して実行されます。 これらの定義済みイベントは、フォームやコントロールなどの GUI クラスのメンバーです。 次のコードに示すように、特定の名前付きイベント (たとえば、Form クラスのClick イベント) を参照し、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。 そのため、イベント ハンドラー メソッドは 1 つのパラメーター (通常はイベント引数) を受け取り、 unitを返します。 前の例では、イベント ハンドラーをラムダ式として示しています。 イベント ハンドラーは、次のコード例のように関数値にすることもできます。 次のコード例では、イベントの種類に固有の情報を提供するイベント ハンドラー パラメーターの使用も示しています。 MouseMove イベントの場合、システムはポインターのX位置とY位置を含むSystem.Windows.Forms.MouseEventArgs オブジェクトを渡します。

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# イベントは、IEvent インターフェイスを実装する F# イベントの種類で表されます。 IEvent 自体は、 System.IObservable<'T>IDelegateEvent という 2 つの他のインターフェイスの機能を組み合わせたインターフェイスです。 したがって、 Eventには、他の言語のデリゲートと同等の機能に加えて、 IObservableの追加機能があります。つまり、F# イベントはイベント フィルター処理をサポートし、F# のファースト クラス関数とラムダ式をイベント ハンドラーとして使用できます。 この機能は、 イベント モジュールで提供されます。

他の .NET Framework イベントと同じように動作するクラスにイベントを作成するには、クラス内のフィールドとしてEventを定義するlet バインドをクラスに追加します。 目的のイベント引数の型を型引数として指定するか、空白のままにしてコンパイラに適切な型を推論させることができます。 また、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 モジュールによって提供される追加機能を次に示します。 次のコード例は、イベントとトリガー メソッドを作成し、ラムダ式の形式で 2 つのイベント ハンドラーを追加し、イベントをトリガーして両方のラムダ式を実行する 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 モジュールには、監視可能なオブジェクトに対して動作する同様の関数が含まれています。 監視可能なオブジェクトはイベントに似ていますが、イベント自体がサブスクライブされている場合にのみ、イベントをアクティブにサブスクライブします。

インターフェイス イベントの実装

UI コンポーネントを開発するときは、多くの場合、既存のフォームまたはコントロールから継承する新しいフォームまたは新しいコントロールを作成することから始めます。 イベントはインターフェイスで頻繁に定義されます。その場合、イベントを実装するにはインターフェイスを実装する必要があります。 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)

こちらも参照ください