Использование средства выбора файлов Xamarin Essentials в приложении Fabulous (F#) UWP

Я тестирую создание приложения UWP с использованием фреймворка Fabulous для написания функциональных кроссплатформенных приложений, и я хочу использовать FilePicker при нажатии кнопки и использовать выбранный файл для некоторой обработки данных.

Выполнение let fileResult = FilePicker.PickAsync() |> Async.AwaitTaskоткрывает средство выбора файлов и возвращает после выбора файла (это означает, что выполняется кнопка и последующий вызов функции), но остальной код, следующий за ней, будет выполнен до того, как можно будет использовать результат. Если я добавлю |> 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, выбрать файл и затем обработать файл в таком приложении?

1 ответ

Решение

Я нашел способ сделать то, что хотел, изучив тестовый пример и документацию 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 по меньшей мере.

Другие вопросы по тегам