在本文中,将分享开发一个.NET2.0图像查看器的经历。这个项目的目标是利用.NET的能力,创建一个图像查看器,它不仅能够管理图像,而且可能因为Mono的存在而具有可移植性。此外,由于缺乏64位的图像查看器,这也促使追求一个.NET解决方案,它在硬件和软件平台允许的情况下,可以隐式地作为64位应用程序运行。
应用程序的设计遵循当代图像查看器的传统风格,具体如下:
在窗口的左侧是一个驱动器列表和相应的目录树;右侧是一个缩略图列表,显示所选驱动器和目录中包含的图像的缩略图。选择一个驱动器会刷新目录列表,并显示根驱动器上包含的图像的缩略图。选择一个特定目录会用相应的缩略图填充缩略图列表。用户可以使用箭头键和滚动条在缩略图列表中导航。
当用户在缩略图上点击左键或按下Enter键时,会打开一个新窗口,其中包含全尺寸的图像,如果图像的任一维度大于相应的屏幕尺寸,则可以滚动。用户可以通过使用空格键和退格键以及箭头键在图像列表中浏览。用户可以通过按下Esc键或窗口上的关闭按钮来关闭窗口。
如果再次点击图像窗口或按下Enter键,它将设置图像为全屏查看,如果需要,会调整大小以适应屏幕。在全屏模式下,用户可以使用空格键和退格键、箭头键和鼠标滚轮来浏览缩略图列表。用户可以通过按下Esc键或鼠标左键退出此模式。
项目结构在下面的图表中揭示。将依次解释每个相关的源代码工件。
类 TasksDispatcher
这是一个通用的任务调度器,它根据可用的处理器核心数量来划分任务数组。在以前的版本中,这个类使用了原始线程。在这个版本中,它依赖于.NET的ThreadPool类,因此,它依赖于池化的线程,最小化了线程创建和销毁的成本。
对于手头的问题,使用任务调度器类来划分(通过它们的索引)要显示的图像缩略图集合,当用户选择一个目录时,这些缩略图将异步显示。
using System;
using System.Threading;
namespace ImageFan.Auxiliary {
internal class TasksDispatcher {
public TasksDispatcher(WaitCallback globalTask, WaitCallback individualTask, int tasksCount) {
_globalTask = globalTask;
_individualTask = individualTask;
_tasksCount = tasksCount;
_isStopped = true;
}
public void Start() {
_dispatcherIsActive = true;
ThreadPool.QueueUserWorkItem(WorkerThreadLoopMethod);
}
public void Stop() {
_dispatcherIsActive = false;
}
public bool IsStopped {
get {
return _isStopped;
}
}
#region Private
private static readonly int ProcessorCount;
private static AutoResetEvent[] IndividualTaskEvents;
private WaitCallback _globalTask;
private WaitCallback _individualTask;
private int _tasksCount;
private volatile bool _dispatcherIsActive;
private volatile bool _isStopped;
private void WorkerThreadLoopMethod(object state) {
try {
_isStopped = false;
for (var i = 0; (i < _tasksCount) && (_dispatcherIsActive); i += ProcessorCount) {
int j;
for (j = 0; (j < ProcessorCount) && (i + j < _tasksCount) && (_dispatcherIsActive); j++) {
_globalTask(i + j);
}
for (j = 0; (j < ProcessorCount) && (i + j < _tasksCount) && (_dispatcherIsActive); j++) {
ThreadPool.QueueUserWorkItem(IndividualTaskWrapper, new TaskParameter(i + j, j));
}
for (j = 0; (j < ProcessorCount) && (i + j < _tasksCount) && (_dispatcherIsActive); j++) {
IndividualTaskEvents[j].Set();
}
}
} catch {
} finally {
_isStopped = true;
}
}
private void IndividualTaskWrapper(object state) {
try {
TaskParameter taskParam = ((TaskParameter)state);
IndividualTaskEvents[taskParam.ThreadIndex].WaitOne();
_individualTask(taskParam.MainIndex);
} catch {
}
}
#endregion
static TasksDispatcher() {
ProcessorCount = Environment.ProcessorCount;
IndividualTaskEvents = new AutoResetEvent[ProcessorCount];
for (var i = 0; i < ProcessorCount; i++) {
IndividualTaskEvents[i] = new AutoResetEvent(false);
}
}
}
}
类 FolderTreeView
这个类是一个Windows表单自定义控件,扩展了TreeView控件。它以树状方式显示所选驱动器上的文件夹(目录),并且当选择一个目录节点时,触发ThumbnailsSequence控件中缩略图的显示。
类 ThumbnailBox
这个类型是一个Windows表单自定义控件,继承自UserControl类。它是一个可变大小的控件,显示一个图像缩略图框,包含图像缩略图本身,并用图像文件名装饰。它的一个最大维度(无论是宽度还是高度)被缩放到ThumbnailSize(200像素),而另一个维度则按比例缩放。
类 ThumbnailsSequence
ThumbnailsSequence类派生自FlowLayout面板,用作所选目录上磁盘上生成的缩略图的容器。
类 ImageForm
ImageForm是一个Windows表单,当用户点击特定的图像缩略图、在目录内通过键盘导航图像或退出全屏模式后,它作为对话框显示。该表单保留了图像大小,如果图像超过屏幕尺寸,则可以滚动。
类 FullScreenImage
这个类是一个Windows表单,作为一个没有表单边框的对话框显示,占据全屏大小,并有一个黑色背景。因此,它给人一种视觉上的错觉,就像是一个具有全屏模式的不寻常的GUI工件。它还调整图像的大小,如果它比全屏可用的尺寸大,同时保持初始的宽高比。
类 ImageFile
ImageFile是一个享元模式类,将图像缩略图存储为其内在状态,而全尺寸图像作为其外在状态,只有在需要时才检索。
类 ImageFolder
这个类提取并管理给定文件夹(目录)内的图像文件。
类 ImageResizer
这个类型有两个操作:调整从文件中提取的图像的大小(用于生成缩略图)和调整从内存中提取的图像的大小(用于全屏模式)。
类 GlobalData
这个类包含对资源图像LoadingImage和InvalidImage的引用,以及它们各自的缩略图。
尽管这个图像查看器是一个托管应用程序,但在浏览或查看速度上(除了特定的优化之外)与原生可执行文件解决方案(如IrfanView、XnView和AcdSee)之间没有差异。这是因为.NET的System.Drawing和System.Windows.Forms类是对WinAPI功能的相当紧密的包装。
Image类,由Bitmap类继承,实现了IDisposable接口,使得享元设计模式的实现变得简单,因为能够以确定性的方式管理图像内存占用。
64位操作系统的改进使得托管编程运行时真正地闪耀。这是因为用托管语言(如.NETCLS兼容的)编写的应用程序在相同的二进制包中固有地支持32位到64位的切换,同时提供运行平台的全部优势。