如何使用Rx和F#将WPF Button.Click事件转换为Observable

时间:2011-02-27 04:01:46

标签: wpf f# system.reactive routedevent

我正在尝试复制一些从IObservable事件创建Button.Click的C#代码。 我想将此代码移植到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 - &gt; RoutedEventArgs - &gt;单位,但这里有类型EventHandler

我在PresentationCore中找到的RoutedEventHandler签名是:

public delegate void RoutedEventHandler(object sender, RoutedEventArgs e);

正如您所看到的,它确实需要objectRoutedEventArgs作为参数,因此F#编译器实际上是正确的。

C#编译器在幕后做了一些神奇的工作来使F#编译器不能正常工作,或者我只是在这里遗漏了什么?

无论哪种方式,我怎样才能在F#中完成这项工作?

2 个答案:

答案 0 :(得分:17)

我知道从WPF Button.Click事件中制作IObservable<_>的最简单方法是投射它:

open System
open System.Windows.Controls

let btn = new Button()
let obsClick = btn.Click :> IObservable<_>

检查obsClick ...

val obsClick : IObservable<Windows.RoutedEventArgs>

这是可能的,因为标准.NET事件的F#表示是类型(在本例中)IEvent<Windows.RoutedEventHandler,Windows.RoutedEventArgs>。正如您在文档中看到的那样,IEvent implements IObservable。换句话说,在F#中,每个单独的事件都是IObservable

答案 1 :(得分:6)

乔尔·穆勒(Joel Mueller)很受欢迎,所以只是为了记录: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)
)