在现代软件开发中,将复杂的任务处理逻辑与用户友好的图形界面(GUI)相结合一直是一个挑战。随着多核处理器的出现和C#中Parallel Task库的引入,有了更多的工具来提高算法的速度和效率。尽管Async CTP和C# 5.0尚未进入生产环境,但寻找一个稳定的进度视图模型(ProgressViewModel),可以应用于C# 4.0,是值得的。
在CodeProject的另一篇文章《可重用的ProgressViewModel以观察MVVM中的进度》中,开发了这个ViewModel。本文将之前的设计进行了扩展,使其能够在这个模式中管理多个进度。
为了保持代码的可读性和一致性,在项目中使用了StyleCop。如果在编译项目时遇到错误,可以下载并安装StyleCop,或者编辑/删除每个.csproj文件中的相应条目。
正在开发一个需要批量处理的应用程序。选择N个项目,每个项目在异步执行的线程中依次处理。为了模拟这种情况,扩展了演示应用程序,使其不仅解析给定目录中的HTML文件,还解析Path目录下所有子目录中的HTML文件。
进度部分有两个进度指示器。一个是MainWindow右上角的“n/m”标签,另一个是进度条。前者表示已解析的目录数量和总目录数量,而后者进度条则像以前一样表示在给定目录中解析HTML文件的进度。
如果系统中没有带有HTML文件和子目录的目录,请下载附带的SampleData。点击第一个MainWindow底部的展开器,将应用程序指向未压缩的文件夹,然后点击“处理HTML”按钮开始处理。
在本文中讨论的ProgressViewModel的扩展是在MultiProgressVM类中进行的(简而言之),替换了简单的属性:ProgressMin、ProgressMax、ProgressValue和IsProgressVisible,用数组:ProgressMin[]、ProgressMax[]、ProgressValue[]和IsProgressVisible[]。类构造函数接受一个int类型的数字,控制这些数组的大小(默认为一个)。然后,视图可以通过绑定到每个属性数组中的一个项目来实现多个进度指示器。
在Visual Studio 2010 Solution Explorer的项目概览中,添加了与之前发布的文章中的ProgressViewModel相比所选的类。为每个层中的每个对象添加了一个额外的类(除了视图和结果类)。这再次展示了这里使用的分层架构的本质。
在视图中的绑定从IsProgressVisible更改为IsProgressVisible[index],以指示数组中的相应项目。在MultiProcessHTML类的ProcessHTMLDirectory方法的代码后台中也可以看到类似的更改。例如,这样的语句:
C#this.ProgressMax = files.Length;
被替换为:
C# this.SetMaxValue(1, files.Length);
以让绑定的UI知道这些值确实已经改变。
MultiProcessHTML类的ProcessHTMLDirectory方法通过内部和外部循环进行迭代。内部循环遍历给定目录中的每个文件,而外部循环遍历给定目录中的每个目录。
除此之外,没有太多要说的,只是强调上述差异,并指出ProgressViewModel现在管理两个进度指示器,而以前只有一个。如果觉得上述解释太短,请确保已经阅读了第一篇文章关于ProgressViewModel。
不确定数组是否真的可以绑定到UI元素。简短的答案是,可以,但不能再通过CLR属性更新值,因为C#不支持命名索引器,被要求在值变化时执行OnPropertyChanged()。在情况下,为每个数组属性实现一个单独的方法,并将每个数组属性的setter设置为private,已经解决了这个问题(认为实现一个带有索引器的单独类是多余的,不值得努力)。
看到新的“n/m”进度指示器让想知道“估计进度指示器”的类型:剩余时间:x分钟。一种简单的方法是记录执行一个内部循环所需的时间,然后将结果乘以剩余的内部循环数量。如果每个循环平均需要相同的进度,这将很好地工作。如果处理所需的努力在内部循环之间变化很大,那么这种方法就不会很好地工作。显然,在这种情况下,获得可靠的估计是一个单独的问题。也许这是本系列文章第三篇的主题...