Использование средства выбора файлов 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
по меньшей мере.