我正在寻找一种关于如何干净地向UI层报告进度的好方法,或者至少是一些见解。
我的情况如下:Repository
中的Infrastructure Layer
与Data Layer
进行通信。虽然这两个正在运行,但UI
目前还没有关于正在发生的事情的线索,只是被动地等待结果(使用Observable)
我的第一个想法是公开UI绑定到的存储库中的事件。但我觉得这可能会变得非常混乱。我很想知道你们是如何解决这个问题的。
答案 0 :(得分:4)
我在后台处理和报告进度方面做了很多工作,所以我完全理解它可以获得多么“混乱”。
我创建了一个开源库,它真的为报告进度提供了一个很好的框架,特别是在“嵌套”场景中!查看Progression。
我建议你看看项目中的一些单元测试,因为那里有很多用法示例。还有一个可运行的演示来查看代码的实际运行情况。
在创建图书馆的过程中,我遇到了一些“混乱”的问题。以下是我解决这些问题的方法:
从多个层报告进度
有一段时间,我正在将一个共同的“进度”对象传递到每个图层,以便每个图层都可以向其报告。这种方法很糟糕。每个方法都必须传递对象,即使它没有使用它
相反,我决定使用一个像单身人士一样的静态类Progress
。这样,每一层都直接报告进度(如果需要)
可以通过附加到事件或通过轮询来监视进度
这种方法使得Progression非常容易报告和阅读进度。
线程安全
当你有一个显示后台任务进度的UI时,通常只需要计算进度,对吧?显然,我必须真正考虑线程安全
但是,我真的想避免使用典型的锁定机制,因为它们很昂贵,我绝对不想放慢已经很慢的后台任务。所以,我的解决方案是使用后台任务创建一个独立的ProgressChangedInfo
对象,可以在线程之间安全地传递。
交叉线程
最初,我的后台进程报告了数千个进度事件,并为每个进程事件调用了UI更新。用户界面会停止,试图赶上所有更新
所以我介绍了“轮询”,但我发现“重要的”进展,例如“复制文件2的3”,正在被放弃,转而支持不太重要的更新,例如“文件5%复制”。
现在,“重要”更新将始终优先于不太重要的更新,因此UI可以尝试尽快更新,始终显示信息最丰富的消息,并且UI将始终保持完全响应。
以下是一些特别酷的功能:
for
循环,每次迭代20次,价值5%IEnumerable<T>.WithProgress()
将在迭代时报告所有进度!以下是一些代码操作:
public void PerformSomeTask() {
// This task has 2 steps; the first step takes about 20%, and the second takes 80%:
Progress.BeginWeightedTask(20, 80);
// Start the first step (20%):
Progress.NextStep();
TaskA();
// Start the second step (80%):
Progress.NextStep();
TaskB();
// Finished!
Progress.EndTask();
}
public void TaskA() {
int count = 20;
// This task has 20 steps:
Progress.BeginFixedTask(count);
for (int i = 0; i < count; i++) {
Progress.NextStep();
// ...
}
Progress.EndTask();
}
public void TaskB() {
// This task has an unknown number of steps
// We will estimate about 20, with a confidence of .5
Progress.BeginUnknownTask(20, .5);
while (database.ReadRow()) {
Progress.NextStep();
// ...
}
Progress.EndTask();
}
答案 1 :(得分:2)
Uese要么是活动,要么是(裸)代表。
由于您可能有多个调用,因此委托参数可能是最佳的(不必从事件中取消注册)。
// Repository
public delegate void ReportProgress(int percentage);
public IEnumerable<X> GetSomeRecords(..., ReportProgress reporter)
{
...;
if (reporter != null)
reporter(i * 100 / totalRecords);
}
答案 2 :(得分:1)
如何返回报告0-100进度的IObservable<int>
?这不仅具有返回进度的优点,而且还可以在出现问题时返回错误(通过OnError)。
答案 3 :(得分:0)
经典的方法是让你的非用户可见图层显示“[Unr | R] egisterProgressCallback()”方法和暴露拟合回调的UI层,现在你的胶水代码可以将它们放在一起。