Xamarin形式:点击事件Args派生类型

时间:2018-11-17 01:38:26

标签: xamarin.forms f#

我有一些可以正常工作的代码:

type App() =
    inherit Application()

    let stack = StackLayout(VerticalOptions = LayoutOptions.Center)
    let label = Label(XAlign = TextAlignment.Center, Text = "Welcome to F# Xamarin.Forms!")

    do
        let tapRecognizer = new TapGestureRecognizer()
        let handleTapEvent (sender:Object) (args:EventArgs) =
            label.Text <- "Tapped at " + DateTime.Now.ToString() 
            ()

        let tapEventHandler = new EventHandler(handleTapEvent)
        tapRecognizer.Tapped.AddHandler(tapEventHandler)
        label.GestureRecognizers.Add(tapRecognizer)

但是,当我将参数从EventArgs更改为这样的派生类型时:

type TapEventArgs(someId:int) = 
          inherit EventArgs()
          member this.SomeId = someId

        let handleTapEvent (sender:Object) (args:TapEventArgs) =
            label.Text <- args.SomeId.ToString() + " tapped"
            ()

调用AddHandler时出现以下错误

  

类型'EventArgs'与类型'TapEventArgs'不兼容

此外,如果我这样更改EventHandler:

let tapEventHandler = new EventHandler<TapEventArgs>(handleTapEvent)

我收到此错误

 This expression was expected to have type 'EventHandler' 
but here has type    'EventHandler<TapEventArgs>'

有什么方法可以强制这种派生类型?

2 个答案:

答案 0 :(得分:2)

F#不会像C#那样自动插入向下转换(这是一件好事,不是bug)。您不能传递预期祖先类型的后代类型。

要呼叫filter,您需要使用下线运算符foreach手动插入下线,如下所示:

AddHandler

当已经知道目标类型时(例如您的情况),您可以在其下划线使用F#编译器从上下文中推断出它:

:>

答案 1 :(得分:1)

这里的问题是类型不同,这就是F#告诉您的。 tapRecognizer.Tapped的类型为EventHandler,因此调用它时,它的类型将为EventHandler,即不是EventHandler<TapEventArgs>,这是另一种类型。无法更改此方法,并且TapGestureRecognizer的子类也无法实现,因为该类是密封的。

此外,您发布的代码将很难编译,因为它具有F#阻止的循环引用。 TapEventArgs要求使用label中定义的AppApp取决于TapEventArgs。无法从TapEventArgs更新UI,而是需要传入一个函数或暴露状态。

有一种使用Command模式的解决方案,它允许将某些信息传递给回调并避免循环依赖。 TapGestureRecognizer具有属性CommandParameter,可以在其中设置值(obj类型)。可以通过Command属性提供回调,该回调可以接收该值。这是完整的示例:

open Xamarin.Forms
open System

type App() as this =
    inherit Application()

    let stack = StackLayout(VerticalOptions = LayoutOptions.Center)
    let label = Label(XAlign = TextAlignment.Center, Text = "Welcome to F# Xamarin.Forms!")

    do
        let tapRecognizer = new TapGestureRecognizer()
        let handleTapEvent (x:obj) = 
            match x with 
            | :? int as someId -> label.Text <- someId.ToString() + " tapped"
            | _ -> label.Text <- "Tapped at " + DateTime.Now.ToString() 

        tapRecognizer.Command <- new Command(Action<obj>(fun x -> handleTapEvent x))
        label.GestureRecognizers.Add(tapRecognizer)
        stack.Children.Add label

        tapRecognizer.CommandParameter <- 42 // The value to be passed to the Command's callback
        this.MainPage <- ContentPage(Content = stack)

请注意,由于Xamarin.Forms中定义类型的方式(使用obj作为属性),因此必须使用强制转换。