65.9K
CodeProject 正在变化。 阅读更多。
Home

MultiProgressViewModel用于观察复杂算法中的进度

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2012年1月20日

CPOL

4分钟阅读

viewsIcon

21281

downloadIcon

933

我最近发表了一篇关于ProgressViewModel的文章,展示了一个可用于C# 4.0中后台处理的软件设计。这篇文章扩展了这个设计,增加了输出多个进度的能力。

引言

管理异步任务并将它们与响应式GUI连接一直是过去的挑战。多核处理器的出现和C#中的并行任务库为我们提供了改进算法以提高速度和效率的工具。Async CTP和C# 5.0尚未正式发布,因此寻找一个可应用于C# 4.0的稳定ProgressViewModel是值得的。我在这篇CodeProject文章中开发了这个ViewModel,题为一个可重用的ProgressViewModel,用于通过MVVM观察进度

本文在前文展示的设计基础上进行了扩展,以便在此模式下管理多个进度。

StyleCop

我在项目中使用了StyleCop,以确保代码的统一性和可读性。因此,如果您在编译项目时遇到错误,可以下载并安装StyleCop,或者编辑/删除每个.csproj文件中的相应条目。

<Import Project="$(ProgramFiles)\MSBuild\StyleCop\v4.5\StyleCop.Targets" />

背景

我正在开发一个需要批量处理的应用程序。选中N个项目,然后(按顺序)在异步执行的线程中处理每个项目。为了模拟这种情况,我扩展了演示应用程序,使其不仅解析给定目录中的HTML文件,还解析Path目录下的所有子目录中的所有HTML文件。

HTMLTitleParser

进度部分有两个进度指示器。右上方MainWindow上的n of m标签和进度条。前者指示已解析的目录及其总数,而后者进度条(如前所述)指示解析任何给定目录中HTML文件的进度。

如果您系统中没有包含HTML文件和更多HTML文件的子目录的目录,请下载附带的SampleData。点击第一个MainWinidow底部的展开器,将应用程序指向解压缩的文件夹,然后点击Process HTML按钮开始处理。

Using the Code

这里讨论的ProgressViewModel的扩展(简而言之)在MultiProgressVM类中,我用数组替换了简单的属性

  • ProgressMin
  • ProgressMax
  • ProgressValue, and
  • IsProgressVisible

替换为数组

  • ProgressMin[]
  • ProgressMax[]
  • ProgressValue[], and
  • IsProgressVisible[]

类构造函数接受一个int数字,该数字控制这些数组的大小(默认为一)。然后,视图可以通过绑定到数组中每个属性的项来实现多个进度指示器。我希望将此扩展与第一种方法分开,因为我希望确保之后能看到差异,并且我仍然希望使用第一个ProgressViewModel文章中的方法。

SolutionExplorer.png

Visual Studio 2010解决方案资源管理器中的项目概述显示,与之前发布的关于ProgressViewModel的文章相比,我添加了选定的类。

我在每个层(除了视图和结果类)中为每个对象添加了一个额外的类。这再次展示了这里使用的架构的分层性质。

视图中的绑定已从

  • IsProgressVisible更改为IsProgressVisible[index]

以指示数组中的相应项。

MultiProcessHTML类中的ProcessHTMLDirectory方法的代码隐藏中可以看到类似的更改。这里,一条语句,如

this.ProgressMax = files.Length;

被替换为

this.SetMaxValue(1, files.Length);

让绑定的UI知道这些值确实已经改变了。

MultiProcessHTML类中的ProcessHTMLDirectory方法会迭代一个内循环和一个外循环。内循环遍历给定目录中的每个文件,外循环遍历给定目录中的每个目录。

关于这一点,没什么更多要说的了,只是强调上述差异,并指出ProgressViewModel现在管理两个进度指示器,而以前只有一个。如果您觉得上面的解释过于简短,请务必阅读我关于ProgressViewModel的第一篇文章。

关注点

我不确定数组是否可以绑定到UI元素。简短的回答是肯定的,但由于C#不支持命名索引器,我们无法再通过CLR属性更新值,并且在值更改时需要执行OnPropertyChanged()。因此,在这种情况下,我通过为每个数组属性实现一个单独的方法并将每个数组属性的setter设置为private来实现(我认为实现一个带有索引器的单独类有点跑题,不值得费力)。

看到新的n of m进度指示器,我开始考虑估计进度指示器,例如:剩余时间:x分钟。一个简单的方法是记录执行一个内循环所需的时间,然后乘以剩余内循环的数量。如果每个循环平均需要相同的时间,这将很好地工作。

如果每个内循环的处理工作量差异很大,那么它将无法很好地工作。显然,在这种情况下,获得可靠的估计是另一个独立的问题。也许这是本系列第三篇文章的主题……

请花点时间通过投票和评论给我您的反馈。

历史

  • 2012年1月20日:第一个版本。
© . All rights reserved.