2

我正在测试使用 Fabulous 框架编写功能性跨平台应用程序的 UWP 应用程序,并且我想在按下按钮时使用 FilePicker 并将所选文件用于某些数据处理。

Executing let fileResult = FilePicker.PickAsync() |> Async.AwaitTask打开文件选择器并在选择Async<FileResult>文件后返回 a (这表示按钮和后续函数调用执行),但它后面的其余代码将在结果可用之前执行。如果我附加|> Async.RunSynchronously它(如预期的那样)阻塞线程并且在出现的窗口中不能选择任何文件,尽管返回值将是 FileResult。

在研究了应该如何完成之后,我意识到应该在主线程上打开文件选择器,这导致我找到了以下表单的解决方案

let getFileResultAsync = 
                async {
                    let tcs = new TaskCompletionSource<FileResult>()
                    Device.BeginInvokeOnMainThread(fun () ->
                        async {
                            let! fileResult = FilePicker.PickAsync() |> Async.AwaitTask
                            tcs.SetResult(fileResult)
                        } 
                        |> Async.StartImmediate
                    )

                    return! tcs.Task |> Async.AwaitTask
                }

这将返回Async<FileResult>,但似乎从未访问过 Device.BeginInvokeOnMainThread 块。我将如何打开 FilePicker,选择一个文件,然后在这样的应用程序中处理该文件?

4

1 回答 1

2

通过进一步研究测试示例和更新和消息的 Fabulous 文档https://fsprojects.github.io/Fabulous/Fabulous.XamarinForms/update.html,我找到了一种方法来做我想做的事情。

基于创建一个新的 Fabulous 项目时生成的标准应用程序,现在只需在模型中有一个给定的字符串,例如文件路径(我称之为 FilePath),然后添加三个附加消息以键入 Msg 为跟随

type Msg =
...
| SelectFile
| PresentFile of FileResult 
| ErrorFileNotSelected

其中第一个在按下用于选择文件的按钮时发送,第二个在选择文件时发送,第三个用于如果用户退出文件对话框而不选择文件。

您需要一个函数来异步选择文件

let selectFileAsync = 
        async {
            let! result = FilePicker.PickAsync() |> Async.AwaitTask
            return result
        } 

和一个 Fabulous.Cmd,它调用上述函数并在程序中进一步发送消息(可能是一种更好的解释方式)

let selectFileCmd = async {
            let! file = selectFileAsync

            match file with 
            | null ->  return Some(ErrorFileNotSelected)
            | _  -> return Some(PresentFile file)
        }

最后,将以下三个模式添加到更新中,其中 selectFileCmd 在 SelectFile 下被调用

let update msg model = 
...
| SelectFile ->
            {model with FilePath = "Selecting file"}, (Cmd.ofAsyncMsgOption selectFileCmd)
| PresentFile file -> 
            {model with FilePath = file.FullPath}, Cmd.none 
| ErrorFileNotSelected -> 
            {model with FilePath = "Error. Must select a file"}, Cmd.none

我不确定这是否被认为是一种好方法,但它似乎let mutable至少比使用更好。

于 2021-01-05T13:38:16.507 回答