我正在尝试更新ProgressBar.Value中的FsXaml。在C#中,我使用了以下代码.我没有尝试在F#中实现F#方法,因为在我看来,使用公共字段(myCaller)并不是一种功能方法(更不用说我不知道在F#中使用这种C#方法是否可能)。
//C# code
namespace Special_technical_dictionary_CSharp_4._011
{
//...some usings
class ExcelData
{
//...some code
public void WritingIntoDat()
{
//...some code
using (bw = new BinaryWriter(new FileStream(...some params...)))
{
while ((currrowIndex < (lastrowIndex + 1)))
{
//...some code
Form1.myCaller.updateProgressBarValue(100 * currrowIndex);
currrowIndex += 1;
}
bw.Close();
}
//...some code
}
}
}
namespace Special_technical_dictionary_CSharp_4._011
{
//...some usings
public partial class Form1 : Form
{
//...some code
public static Form1 myCaller;
public Form1()
{
InitializeComponent();
myCaller = this;
}
//...some code
public void updateProgressBarValue(int valueV)
=> progressBar.Value = (progressBar.Value == progressBar.Maximum) ? valueV : 0;
//...some code
}
}我的问题是:在更新ProgressBar.Value的F# (FsXaml/代码后面)中,什么是最好的(或者至少是好的)功能方法?
EDIT1:
删除不相关的代码和文本。那些对Elmish.WPF不感兴趣的人,请等到FsXaml的答案出现。
EDIT2:
Elmish.WPF
我试图使用本特兰伯格的评论和答案以及他的优秀示例代码来处理ProgressBar问题。我的适应性适用于for -循环,但不适用于List.map(i)/iter(i),这是我实际上需要进度条的集合函数。以下是简化的代码:
文件: MainWindow.fs
//F# code
module Elmish.MainWindow
type ProgressIndicator = Idle | InProgress of percent: int
type Model =
{
ProgressIndicatorLeft: ProgressIndicator
ProgressIndicatorRight: ProgressIndicator
}
let initialModel =
{
ProgressIndicatorLeft = Idle
ProgressIndicatorRight = Idle
}
let init() = initialModel, Cmd.none
type Msg =
| UpdateStatusLeft of progress: int
| WorkIsCompleteLeft
| UpdateStatusRight of progress: int
| WorkIsCompleteRight
| TestButtonLeftEvent
| TestButtonRightEvent
// FOR TESTING PURPOSES ONLY
let private longRunningOperationLeft dispatch = //simulating long running operation
async
{
for i in 1..100 do
do! Async.Sleep 20
dispatch (UpdateStatusLeft i) //THIS WORKS
dispatch WorkIsCompleteLeft
}
// FOR TESTING PURPOSES ONLY
let private longRunningOperationRight dispatch = //simulating long running operation
async //NOT WORKING
{
[1..10000]
|> List.mapi(fun i item ->
[1..100] |> List.reduce (*) |> ignore
dispatch(UpdateStatusRight i)
)
dispatch WorkIsCompleteRight
}
let update (msg: Msg) (m: Model) : Model * Cmd<Msg> =
match msg with
| UpdateStatusLeft progress -> { m with ProgressIndicatorLeft = InProgress progress; ProgressBackgroundLeft = Brushes.White }, Cmd.none
| WorkIsCompleteLeft -> { m with ProgressIndicatorLeft = Idle; ProgressBackgroundLeft = Brushes.LightSkyBlue }, Cmd.none
| UpdateStatusRight progress -> { m with ProgressIndicatorRight = InProgress progress; ProgressBackgroundRight = Brushes.White }, Cmd.none
| WorkIsCompleteRight -> { m with ProgressIndicatorRight = Idle; ProgressBackgroundRight = Brushes.LightSkyBlue }, Cmd.none
| TestButtonLeftEvent ->
let incrementDelayedCmd (dispatch: Msg -> unit) : unit = //THIS WORKS
let delayedDispatch = longRunningOperationLeft dispatch
Async.StartImmediate delayedDispatch
{ m with ProgressIndicatorLeft = InProgress 0 }, Cmd.ofSub incrementDelayedCmd
| TestButtonRightEvent ->
let incrementDelayedCmd (dispatch: Msg -> unit) : unit = //NOT WORKING
let delayedDispatch = longRunningOperationRight dispatch
Async.StartImmediate delayedDispatch
{ m with ProgressIndicatorRight = InProgress 0 }, Cmd.ofSub incrementDelayedCmd
let bindings(): Binding<Model,Msg> list =
[
"ProgressLeftBackg" |> Binding.oneWay(fun m -> m.ProgressBackgroundLeft)
"ProgressRightBackg" |> Binding.oneWay(fun m -> m.ProgressBackgroundRight)
"ProgressLeft" |> Binding.oneWay(fun m -> match m.ProgressIndicatorLeft with Idle -> 0.0 | InProgress v -> float v)
"ProgressRight" |> Binding.oneWay(fun m -> match m.ProgressIndicatorRight with Idle -> 0.0 | InProgress v -> float v)
"TestButtonLeft" |> Binding.cmdIf(TestButtonLeftEvent, fun m -> match m.ProgressIndicatorLeft with Idle -> true | _ -> false)
"TestButtonRight" |> Binding.cmdIf(TestButtonRightEvent, fun m -> match m.ProgressIndicatorRight with Idle -> true | _ -> false)
]即使将"i“索引与进度条值绑定对于MainWindow中的集合函数也是有效的,它也不会解决这个问题。在现实生活中,用于处理进度条值的集合函数位于“以上”主窗口文件中。如下所示:
文件: MainLogicRight.fs
//F# code
module MainLogicRight
let textBoxString3 low high path =
//some code
let myArray() =
Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)
|> Option.ofObj
|> optionToArraySort "..." "..."
|> Array.collect
(fun item ->
let arr =
let p = prefix + "*"
Directory.EnumerateDirectories(item, p)
|> Option.ofObj
|> optionToArraySort "..." "..."
|> Array.Parallel.mapi(fun i item ->
let arr = Directory.EnumerateFiles(item, "*.jpg", SearchOption.TopDirectoryOnly)
|> Option.ofObj
|> optionToArraySort "..." "..."
arr.Length
)
arr
) 我了解到,(可能)不可能将pb值绑定到非索引函数(如Array.collect )。但重要的是,如何将pb值与List/Array.mapi/iteri (Array.Parallel.mapi 中的"i“索引绑定(在本例中) ?
EDIT3:
根据本特的最后一个答复,我删除了现在不相关的文本和评论。基于答案的一个例子是这里。
发布于 2021-12-04 21:43:52
这个答案解释了如何在Elmish.WPF中从异步完成对用户界面的进度更新。
我已经创建了一个GitHub上的实例来演示这个。该示例还演示了调用异步函数和接收结果的另一种方法。它还演示了如何使用mkProgram而不是mkSimple。演示可以用作Elmish.WPF应用程序的起始模板。
演示中的这个片段显示了从异步更新用户界面所涉及的基本代码。
这两种技术都基于埃尔米什书的代码。在Elmish.WPF中,您会发现很多代码也很有用。
我没有尝试在这里更新一个进度条,只是一个状态栏文本框,但是从这里你可以很容易地知道该做什么来更新任何东西。
| UpdateStatusText statusText ->
{ m with StatusText = statusText }, Cmd.none
| RunWithProgress ->
let incrementDelayedCmd (dispatch: Msg -> unit) : unit =
let delayedDispatch = async {
do! Async.Sleep 1000
dispatch (UpdateStatusText "One")
do! Async.Sleep 1000
dispatch (UpdateStatusText "Two")
do! Async.Sleep 1000
dispatch (UpdateStatusText "Three")
}
Async.StartImmediate delayedDispatch
{ m with StatusText = "Started progress." }, Cmd.ofSub incrementDelayedCmd更新:
我现在已经更新了GitHub上的演示项目,以便从异步中演示进度条(和状态文本)的更新。这些都是重要片段的片段。
从异步发送的两个消息的声明。
| UpdateStatus of statusText:string * progress:int
| WorkIsComplete // This message could carry a result from the work done.处理这两条消息。
| UpdateStatus (statusText, progress) ->
{ m with StatusText = statusText; Progress = progress }, Cmd.none
| WorkIsComplete ->
{ m with StatusText = "Work was completed."; Progress = 0 }, Cmd.none
| RunWithProgress ->
let incrementDelayedCmd (dispatch: Msg -> unit) : unit =
let delayedDispatch = async {
do! Async.Sleep 1000
dispatch (UpdateStatus ("Early work", 30))
do! Async.Sleep 1000
dispatch (UpdateStatus ("Still working", 60))
do! Async.Sleep 1000
dispatch (UpdateStatus ("Late work", 90))
do! Async.Sleep 1000
dispatch WorkIsComplete
}
Async.StartImmediate delayedDispatch
{ m with StatusText = "Started progress." }, Cmd.ofSub incrementDelayedCmd字段进度声明为int。
Progress: intProgressBar的属性值是浮点数,因此绑定中需要对浮点数进行强制转换。
"Progress" |> Binding.oneWay (fun m -> float m.Progress)当然,我们可以将模型中的进步声明为浮动,但我想借此机会指出,模型不必与组件属性的数据类型保持一致。当然,我们可以在绑定中以我们想要的任何方式绘制地图。
调度员的最后一个注意事项。这可以通过Cmd.ofSub访问,也可以通过WkProgram.Subscribe访问。在另一种情况下,可能会有更多的信息,但是现在请注意:使用dispatcher发送消息是线程安全的。这意味着您可以向模型发送进度消息(或任何消息),也可以从在顶级异步函数中运行的异步函数发送进度消息(或任何消息),也可以通过计时器事件或任何真正的地方发送。
最后更新: GitHub上的演示现在比这里显示的稍微先进一些,但是原理仍然相同,所以我不会费心在这个答案中更新源代码。任何对此感兴趣的人都很可能需要完整的演示源代码,除非您已经熟悉了Elmish.WPF
问题的最后一部分(稍后添加)在这里得到了回答.
当做冗长和/或CPU密集型的工作时,这应该按照下面的longRunningOperationLeft函数所示进行。这也显示了其他不依赖于GUI的函数是如何编写的,这样就可以将进度更新发送到GUI。
下面所示的longRunningOperationRight是错误的,阻塞了GUI。
我在异步和任务方面的专门知识不是很好,但我认为从Elmish调用的顶级异步函数(如longRunningOperationLeft)运行在与Elmish循环相同的线程上,这就是为什么不应该用任何冗长或CPU密集型的东西来阻止它们。相反,这种阻塞工作需要进入子计算(如workToDo)。longRunningOperationLeft的作用是等待工作,而不是自己工作,以免阻塞GUI。
我不知道List.mapi内部是否可以有异步操作。我怀疑不是。不管怎么说,我想这对你的现实生活来说是不必要的。
米拉的更新:你是对的。在我的现实生活中不需要。在List/array.mapi 中添加 reportProgress i (如在代码中)就足够了。
let private lengthyWork () =
[1..20_000_000] |> List.reduce ( * ) |> ignore
let private workToDo reportProgress = async {
reportProgress 0
lengthyWork ()
reportProgress 25
lengthyWork ()
reportProgress 50
lengthyWork ()
reportProgress 75
lengthyWork ()
reportProgress 100
return 7
}
// This is good.
let private longRunningOperationLeft dispatch = async {
let reportProgress progress = dispatch (UpdateStatusLeft progress)
let! hardWork = Async.StartChild (workToDo reportProgress)
do! Async.Sleep 1000 // Can do some async work here too, while waiting for hardWork to finish.
let! result = hardWork
dispatch WorkIsCompleteLeft
}
// This is not good. Blocking GUI.
let private longRunningOperationRight dispatch = async {
dispatch (UpdateStatusRight 0)
lengthyWork ()
dispatch (UpdateStatusRight 25)
lengthyWork ()
dispatch (UpdateStatusRight 50)
lengthyWork ()
dispatch (UpdateStatusRight 75)
lengthyWork ()
dispatch (UpdateStatusRight 100)
dispatch WorkIsCompleteRight
}https://stackoverflow.com/questions/70208381
复制相似问题