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

问题描述:

我试图复制一些从Button.Click事件创建IObservable的C#代码。 我想将此代码移植到F#。如何使用Rx和F将WPF Button.Click事件转换为Observable#

这里是原来的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

在th另一方面C#编译器似乎也没有问题接受h => new RoutedEventHandler(h)

有趣的是,这两个代码的样品(C#和F#)的h类型是EventHandler<RoutedEventArgs>英寸

我从F#编译器得到的错误信息是:

错误2,预计这种表达有型的obj - > RoutedEventArgs - >单元,但这里有EventHandler类型

的为RoutedEventHandler,我发现里面PresentationCore签名是:

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

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

C#编译器在幕后做了些什么以使F#编译器不工作或我只是在这里丢失什么?

无论哪种方式,我如何使这项工作在F#?

我知道的最简单的方法,使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> 

这是可能的,因为标准.NET事件的F#表示是类型(在本例中)为IEvent<Windows.RoutedEventHandler,Windows.RoutedEventArgs>。正如你可以从文档中看到的那样,IEvent implements 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) 
) 
+0

谢谢,看着反射的C#代码让我意识到,“H”实际上得到扩大到'h。Invoke',然后将其视为代表'(s,e)=> h.Invoke(s,e)'的方法组,从而满足RoutedEventHandler签名。一旦明确,很明显,你用你建议的F#实现就是正确的。 我也同意,Joel Mueller建议的较为详细的转换是首选,除非您需要直接获取发件人的持有者(例如,不通过args.Source),因为它只返回RoutedEventArgs的IObservable。 – 2011-02-27 17:31:02