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

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

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


person MechMayhem    schedule 04.01.2021    source источник
comment
Возможно ли, что внутренний блок async{...} не требуется? В документации ничего не говорится об асинхронном теле лямбды в BeginInvokeOnMainThread. Device.BeginInvokeOnMainThread(fun() -> let fileResult = FilePicker.PickAsync() |› Async.AwaitTask tcs.SetResult(fileResult) )   -  person Piotr Rodak    schedule 04.01.2021
comment
Это, кажется, не помогает, к сожалению. Та же проблема с лямбдой в BeginInvokeOnMainThread, которая, по-видимому, вообще не выполняется.   -  person MechMayhem    schedule 05.01.2021


Ответы (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.

person MechMayhem    schedule 05.01.2021