【问题标题】:Xamarin Forms: Tap Event Args Derived TypeXamarin Forms:点击事件参数派生类型
【发布时间】:2018-11-17 01:38:26
【问题描述】:

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

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)

但是,当我将 args 从 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>'

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

【问题讨论】:

    标签: xamarin.forms f#


    【解决方案1】:

    F# 不会像 C# 那样自动插入向下转换(这是一件好事,而不是错误)。您不能在需要祖先类型的地方传递后代类型。

    要调用AddHandler,您需要使用向下转换运算符:&gt; 手动插入向下转换,如下所示:

    tapRecognizer.Tapped.AddHandler(tapEventHandler :> EventHandler<EventArgs>)
    

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

    tapRecognizer.Tapped.AddHandler(tapEventHandler :> _)
    

    【讨论】:

    • 在 AddHandler 方法中添加 downcast 不起作用。 tapRecognizer.Tapped.AddHandler(tapEventHandler :> EventHandler) throws 这个表达式应该有类型'EventHandler',但这里有类型'EventHandler'
    • tapRecognizer.Tapped的类型是什么?
    【解决方案2】:

    这里的问题是类型不同,这就是 F# 告诉你的。 tapRecognizer.Tapped 的类型是EventHandler,所以当它被调用时,它将是EventHandler 类型,即不是EventHandler&lt;TapEventArgs&gt;,这是一个不同的类型。没有办法改变这一点,并且子类化TapGestureRecognizer 也是不可能的,因为类是密封的。

    此外,您发布的代码很难编译,因为它具有 F# 阻止的循环引用。 TapEventArgs 需要在 App 中定义的 labelApp 取决于 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)。

    【讨论】:

      猜你喜欢
      • 2018-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      • 2016-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多