无法使用 Xamarin.Forms 中的 F# 异步支持观察页面导航



我无法观察页面导航。

命令:

member x.AddBankcard =    DelegateCommand( (fun _ -> async { do! navigate() 
} |> Async.RunSynchronously 
|> ignore ) ,
fun _ -> true) :> ICommand

发布导航请求:

let navigate() =
account 
|> PageRequest.AddBankcard 
|> broadcastToAsync pageRequesthandlers     

通知订阅者请求:

let broadcastToAsync<'a> (handlers:(list<'a -> Async<unit>>)) (msg:'a) : Async<unit> =
async { handlers |> List.map (fun handle -> async { do! handle msg }) |> ignore }

处理导航:

let handle = function
| PageRequest.AddBankcard _ ->
page |> function 
| :? UI.AddBankcard as pageRequest ->
...
async { do! navigationPage.PushAsync(pageRequest) |> Async.AwaitTask }
| _ -> async { () }
| _ -> async { () }

注意:

  1. 不发生页面导航。

  2. 我没有收到任何例外。

  3. 我在输出窗口中看不到任何线索

  4. 我的预感是我没有正确使用异步。

    async { do! navigationPage.PushAsync(pageRequest( |> Async.AwaitTask }

更新:

我还尝试了以下方法:

let navigationPage = (app:?>Application).MainPage:?>NavigationPage
let navigate () = async  {
do! navigationPage.PushAsync(pageRequest) |> awaitTask
}
navigate() |> Async.RunSynchronously |> ignore

这是源代码

仅当代码必须在异步/任务操作完成后执行时,才需要async {...}块。

对于导航,之后通常没有任何操作(某些 MVVM 框架除外(。ignore是必需的,因为函数签名要求返回单位,导航方法返回Task。在 C# 中,Task在调用时启动,而 F# 中的async块则不启动。

下面的代码是一个非常简单的导航示例,不需要async关键字或Async.AwaitTask等。

open Xamarin.Forms
open Xamarin.Forms.Xaml
type NextPage(app:Application) = 
inherit ContentPage() 
let button = Button(Text = "Back")
do 
button.Clicked.Add (fun _ -> app.MainPage.Navigation.PopAsync () |> ignore )
base.Content <- nextButton
type MainPage(app:Application) =
inherit ContentPage()
let nextButton = Button(Text = "Next")
do 
nextButton.Clicked.Add (fun _ -> 
let nextPage = NextPage app
app.MainPage.Navigation.PushAsync nextPage |> ignore )
base.Content <- nextButton
type App() as this =
inherit Application()
do this.MainPage <- NavigationPage(MainPage(this))

我可以告诉你,函数broadcastToAsync并没有真正执行任何操作:

let broadcastToAsync<'a> (handlers:(list<'a -> Async<unit>>)) (msg:'a) : Async<unit> =
async { handlers |> List.map (fun handle -> async { do! handle msg }) |> ignore }

原因是它创建了一个async列表,然后ignore它。

我建议您将List.map更改为List.iter删除|> ignore并调用Async.RunSynchronously

let broadcastToAsync handlers msg =
handlers |> List.iter (fun handle -> handle msg |> Async.RunSynchronously )
"msg" |> broadcastToAsync [ 
fun msg -> async { printfn "1: %A" msg}
fun msg -> async { printfn "2: %A" msg} ] 
// 1: "msg"
// 2: "msg"

以下代码有效:

let navigationPage = (app:?>Application).MainPage:?>NavigationPage
navigationPage.PushAsync(pageRequest) |> Async.AwaitTask |> ignore

下面是一些用于从堆栈中弹出页面的代码:

app |> function
| :? Application as xForms -> 
let navigationPage = xForms.MainPage:?>NavigationPage
navigationPage.PopAsync() |> Async.AwaitTask |> ignore

最新更新