在 Fabulous (F#) UWP 应用程序中使用 Xamarin Essentials 文件选择器
Using Xamarin Essentials File Picker in Fabulous (F#) UWP app
我正在测试使用用于编写功能性跨平台应用程序的 Fabulous 框架制作 UWP 应用程序,我想在按下按钮时使用 FilePicker 并使用选定的文件进行一些数据处理。
正在执行
let fileResult = FilePicker.PickAsync() |> Async.AwaitTask
打开文件选择器并在选择文件后 returns a Async<FileResult>
(这表示按钮和后续函数调用执行),但它后面的其余代码将执行在结果可以使用之前。如果我追加 |> Async.RunSynchronously
它(如预期的那样)阻塞线程并且在出现的 window 中无法选择任何文件,尽管 return 值将是 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
}
将 return Async<FileResult>
,但似乎从未访问过 Device.BeginInvokeOnMainThread 块。我将如何打开 FilePicker,选择一个文件,然后在这样的应用程序中处理该文件?
通过进一步查看测试示例和更新和消息的精彩文档,我想出了一种方法来做我想做的事情 https://fsprojects.github.io/Fabulous/Fabulous.XamarinForms/update.html。
基于创建新的 Fabulous 项目时生成的标准应用程序,只需在模型中有一个给定的字符串,例如文件路径(我称之为 FilePath),并添加三个额外的消息以键入 Msg,如下所示
type Msg =
...
| SelectFile
| PresentFile of FileResult
| ErrorFileNotSelected
第一个在按下 select 文件按钮时发送,第二个在 selected 时与文件一起发送,第三个用于用户退出没有 select 文件的文件对话框。
您需要一个函数来 select 文件异步
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
更好。
我正在测试使用用于编写功能性跨平台应用程序的 Fabulous 框架制作 UWP 应用程序,我想在按下按钮时使用 FilePicker 并使用选定的文件进行一些数据处理。
正在执行
let fileResult = FilePicker.PickAsync() |> Async.AwaitTask
打开文件选择器并在选择文件后 returns a Async<FileResult>
(这表示按钮和后续函数调用执行),但它后面的其余代码将执行在结果可以使用之前。如果我追加 |> Async.RunSynchronously
它(如预期的那样)阻塞线程并且在出现的 window 中无法选择任何文件,尽管 return 值将是 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
}
将 return Async<FileResult>
,但似乎从未访问过 Device.BeginInvokeOnMainThread 块。我将如何打开 FilePicker,选择一个文件,然后在这样的应用程序中处理该文件?
通过进一步查看测试示例和更新和消息的精彩文档,我想出了一种方法来做我想做的事情 https://fsprojects.github.io/Fabulous/Fabulous.XamarinForms/update.html。
基于创建新的 Fabulous 项目时生成的标准应用程序,只需在模型中有一个给定的字符串,例如文件路径(我称之为 FilePath),并添加三个额外的消息以键入 Msg,如下所示
type Msg =
...
| SelectFile
| PresentFile of FileResult
| ErrorFileNotSelected
第一个在按下 select 文件按钮时发送,第二个在 selected 时与文件一起发送,第三个用于用户退出没有 select 文件的文件对话框。
您需要一个函数来 select 文件异步
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
更好。