Как преобразовать событие WPF Button.Click в Observable, используя Rx и F#
Я пытаюсь скопировать некоторый код C#, который создает IObservable
из Button.Click
событие. Я хочу перенести этот код на F#.
Вот оригинальный код C#, который компилируется без ошибок:
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
h => new RoutedEventHandler(h),
h => btn.Click += h,
h => btn.Click -= h))
Вот моя неудачная попытка сделать то же самое в F#:
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
Func<EventHandler<RoutedEventArgs>, RoutedEventHandler>(fun h -> RoutedEventHandler(h)),
Action<RoutedEventHandler>(fun h -> btn.Click.AddHandler h),
Action<RoutedEventHandler>(fun h -> btn.Click.RemoveHandler h))
Все устраивает, кроме второй строки заявления.
Компилятор F# жалуется на fun h -> RoutedEventHandler(h)
потому что не хочет, кроме h
в качестве параметра к RoutedEventHandler
конструктор.
С другой стороны, компилятор C#, похоже, не имеет проблем с принятием h => new RoutedEventHandler(h)
Интересно, что в обоих примерах кода (C# и F#) тип h
является EventHandler<RoutedEventArgs>
,
Я получаю сообщение об ошибке от компилятора F#:
Ошибка 2 Предполагалось, что это выражение имеет тип obj -> RoutedEventArgs -> unit, но здесь имеет тип EventHandler
Подпись для RoutedEventHandler
что я нашел внутри PresentationCore:
public delegate void RoutedEventHandler(object sender, RoutedEventArgs e);
Как вы можете видеть, это занимает object
а также RoutedEventArgs
как параметры, так что компилятор F# на самом деле правильный.
Есть ли какая-то магия, которую компилятор C# делает за кулисами, чтобы заставить эту работу работать, чего не делает компилятор F#, или я просто что-то здесь упускаю?
В любом случае, как я могу заставить это работать в F#?
2 ответа
Самый простой способ, которым я знаю, чтобы сделать IObservable<_>
из события WPF Button.Click это разыграть его:
open System
open System.Windows.Controls
let btn = new Button()
let obsClick = btn.Click :> IObservable<_>
Изучение obsClick...
val obsClick : IObservable<Windows.RoutedEventArgs>
Это возможно, потому что F# представление стандартных событий.NET является типом (в данном случае) IEvent<Windows.RoutedEventHandler,Windows.RoutedEventArgs>
, Как видно из документации, IEvent реализует IObservable. Другими словами, в F# каждое отдельное событие уже является IObservable
,
Джоэл Мюллер на месте, так что просто для записи: прямой перевод кода C# будет
Observable.FromEvent(
(fun h -> RoutedEventHandler(fun sender e -> h.Invoke(sender, e))),
(fun h -> b.Click.AddHandler h),
(fun h -> b.Click.RemoveHandler h)
)