.NET 2.0 图像查看器开发经验分享

在本文中,将分享开发一个.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位的切换,同时提供运行平台的全部优势。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485