的imageview类支持无限数量的项目通过用户的视图布局(参考图像查看器布局).有了这种支持,查看器可以用来加载具有大量页面和较大物理图像大小的文档。当然,在物理内存中可以保存多少图像是有硬件限制的,而图像查看器提供了Virtualizer机制来支持按需加载/卸载图像数据。
例如,图像查看器正在使用带有连续滚动的垂直布局。用户正在尝试加载一个有1000页的PDF文件,每一页都是典型的8.5乘11英寸的文档大小。假设页面是每像素24位,那么像素大小为2550 * 3300,每一页未压缩数据的物理大小为24MB,总共需要24MB * 1000 = 24GB的物理内存来保存所有这些页面。
在上面的垂直布局中,页面(每个页面包含在一个ImageViewerItem对象)将不会同时在屏幕上全部可见。事实上,当用户滚动或缩小查看器时,它们中的大多数将保持不可见状态,直到它们进入视图。查看器需要关于条目数量和每个条目的物理大小的信息,以便进行布局和滚动条计算。但是图像数据本身是不需要的,除非项目是可见的。一个更好的方法是直到项目即将在视图中可见时才加载图像数据。以及当项目从视图中消失且不可见时丢弃图像数据。的ImageViewerVirtualizer执行的就是这个。它是一个抽象类,允许您根据需要轻松加载和卸载项数据,并完全控制呈现占位符和控制要在内存中缓存的项的数量。
我们首先需要的是一份有很多页的文件。下面是使用LEADTOOLS创建这样一个文档的代码:
私人静态无效CreateTestDocument ()
{
//创建一个400页的PDF文件每一页长8.5英寸,宽11英寸
var宽度= 8.5;
var身高= 11.0;
var分辨率= 300;
varpixelWidth = (int)(宽度*分辨率+ 0.5);
varpixelHeight = (int)(高度*分辨率+ 0.5);
varpageccount = 400;
var文件名=字符串.Format (@“C: \ LEADTOOLS21 \资源\图片\ {0}Pages.pdf”, pageCount);
如果(File.Exists(文件名)
File.Delete(文件名);
使用(var编解码器=新RasterCodecs ())
{
varpageMode = codecssavepagemode .覆盖;
为(varpageNumber = 1;pageNumber <= pageCount;pageNumber + +)
{
使用(varrasterImage = CreatePageImage(pixelWidth, pixelHeight,分辨率,pageNumber))
{
编解码器。保存(rasterImage, fileName, RasterImageFormat。RasPdfLzw, 24, 1,1, -1, pageMode);
pageMode = CodecsSavePageMode.Append;
}
}
}
}
私人静态RasterImage CreatePageImage (intpixelWidth,intpixelHeight,int决议,intpageNumber)
{
钢笔[]钢笔=
{
笔。红色,
笔。绿色,
笔。蓝色,
};
Brush[]画笔=
{
刷子。红色,
刷子。绿色,
刷子。蓝色的
};
钢笔钢笔=钢笔[pageNumber % 3];
画笔画笔=画笔[pageNumber % 3];
varrasterImage = rasterImage。创建(pixelWidth, pixelHeight, 24,分辨率,RasterColor.FromKnownColor(RasterKnownColor.White));
varhdc = RasterImagePainter.CreateLeadDC(rasterImage);
使用(vargraphics = graphics . frommhdc (hdc))
{
varrc =新矩形(0,0,pixelWidth, pixelHeight);
图形。DrawRectangle(笔,rc。X, rc。Y, rc。宽度- 2 rc。高度- 2);
图形。DrawRectangle(笔,rc。X + 1 rc。Y + 1 rc。宽度- 3 rc。高度- 3);
var文本=字符串.Format (“页面{0}”, pageNumber);
使用(var字体=新字体(“天线”, 72, FontStyle.Regular))
使用(var科幻小说=新StringFormat ())
{
科幻小说。对齐= stringalign . center;
科幻小说。linealign = stringalign . center;
vartextSize =图形。MeasureString(文本、字体);
浮动Y = 10;
浮动X = 10;
而(y < pixelHeight)
{
而(x < pixelWidth)
{
图形。DrawString(文本,字体,笔刷,x, y);
x += textSize。宽度+ 80;
}
y += textSize。身高+ 80;
X = 10;
}
}
}
RasterImagePainter.DeleteLeadDC (hdc);
返回rasterImage;
}
后创建400页的简历PDF文档,创建一个垂直视图布局的图像查看器:
//创建一个垂直布局的新图像查看器实例
ImageViewerViewLayout新ImageViewerVerticalViewLayout ();
ImageViewer =新imageview (viewLayout);
//设置一些属性
imageview。Dock = DockStyle.Fill;
imageview。BackColor = Color.Bisque;
//添加边框(也需要一些填充)
imageview。ImageBorderThickness = 1;
imageview。ItemPadding =新填充(imageViewer.ImageBorderThickness);
//观看时考虑到图像的分辨率,因此图像大小为8.5 * 11英寸
//当缩放为1:1时,屏幕将显示8.5 x11英寸
imageview。UseDpi =真正的;
//添加到表单中
这.Controls.Add (imageview);
imageViewer.BringToFront ();
//增加平移/缩放交互模式单击并拖动以平移图像,然后按住鼠标单击
//和拖动来放大/缩小
imageViewer.InteractiveModes.Add (新ImageViewerPanZoomInteractiveMode ());
让我们尝试在查看器中按原样加载该文档。这段代码将把所有页面作为项添加到文档中:
var文件名=@ " C: \ LEADTOOLS21 \资源\ \ 400 pages.pdf图像”;
使用(var编解码器=新RasterCodecs ())
{
codecs.Options.RasterizeDocument.Load.Resolution = 300;
//不更新,直到我们有所有的页面
imageViewer.BeginUpdate ();
//获取页面数
intpageCount = codecs.GetTotalPages(文件名);
为(varpageNumber = 1;pageNumber <= pageCount;pageNumber + +)
{
var项=新ImageViewerItem ();
项。图像=编解码器。负载(文件名,pageNumber);
imageViewer.Items.Add(项);
}
imageViewer.EndUpdate ();
}
试着运行这段代码。在花费大量时间加载所有页面后,它将在64位系统上工作,并且很可能在32位系统上出现内存不足异常而失败。系统不能一次将所有这些映像数据加载到内存中。显然,我们需要一个更好的方法。
如在图像查看器项目,图像查看器支持创建没有图像数据的项目。所有的观众需要的是项目的数量以及它们的大小(像素和分辨率)。因此,让我们修改代码,添加没有图像数据的项:
var文件名=@ " C: \ LEADTOOLS21 \资源\ \ 400 pages.pdf图像”;
使用(var编解码器=新RasterCodecs ())
{
codecs.Options.RasterizeDocument.Load.Resolution = 300;
//不更新,直到我们有所有的页面
imageViewer.BeginUpdate ();
//获取每页的页数和大小
intpageCount;
LeadSize图象尺寸;
LeadSizeD决议;
使用(var信息=编解码器。GetInformation(文件名,真正的))
{
//获取页面数
pageCount = info.TotalPages;
//获取像素大小
imageSize = LeadSize.Create宽度,info.Height);
//获取分辨率
决议= leadsize . create(信息。XResolution info.YResolution);
}
为(varpageNumber = 1;pageNumber <= pageCount;pageNumber + +)
{
var项=新ImageViewerItem ();
//设置查看器创建正确布局所需的成员
项。ImageSize = ImageSize;
项。Resolution = Resolution;
//添加
imageViewer.Items.Add(项);
}
imageViewer.EndUpdate ();
}
运行这段代码,并注意到查看器几乎立即启动并运行。添加正确大小的空白页,您可以自由地缩放和平移图像。
最后一步是根据需要填充图像数据。这可以通过创建一个映像虚拟器派生类来轻松完成,该类可以根据需要处理映像数据的加载和释放。
创建一个新类并添加以下代码:
//从抽象ImageViewerVirtualizer派生的类
公共类MyVirtualizer: ImageViewerVirtualizer
{
公共MyVirtualizer() {}
//包含大图的文件
私人字符串_fileName;
公共MyVirtualizer (字符串文件名):
基地()
{
//保存文件
_fileName =文件名;
//保存在内存中的缓存项的数量,默认为16。改为4
这.MaximumItems = 4;
}
受保护的覆盖对象LoadItem (ImageViewerItem项)
{
//当项目进入视图时调用此方法
//并且不是缓存在内存中
//对于这个例子,我们所需要的只是加载图像
//从原始文件。但是我们也可以装载其他的
//状态和数据从数据库或使用反序列化。
//在这个例子中,项目索引是页面索引
//但是,我们可以使用item .Tag属性或派生我们的
//拥有自己的类来保存加载页面所需的数据
//索引是基于0的,所以加1得到页码
varpageNumber =这.ImageViewer.Items.IndexOf(item) + 1;
//加载页面并返回
使用(var编解码器=新RasterCodecs ())
{
codecs.Options.RasterizeDocument.Load.Resolution = 300;
返回编解码器。加载(_fileName, 0, CodecsLoadByteOrder.)BgrOrGray, pageNumber, pageNumber);
}
}
受保护的覆盖无效SaveItem (ImageViewerItem项,对象数据)
{
//当一个项目将要被删除时,这个方法被调用
//从缓存。在这个例子中,我们没有任何事情要做
//如果你的应用程序需要序列化,你可以修改代码
//数据到磁盘或数据库
}
受保护的覆盖无效DeleteItem (ImageViewerItem项,对象数据)
{
//当项目不再使用时调用此方法
//在这个例子中,我们简单地处置我们加载的RasterImage
var图片=数据作为RasterImage;
如果(图片! =零)
image.Dispose ();
}
受保护的覆盖无效RenderItemPlaceholder (ImageViewerRenderEventArgs e)
{
//该方法在加载项目时被调用,并给我们一个机会
//为用户提供提示
//让我们渲染一个Loading…项目信息
var变换=这.ImageViewer.GetItemImageTransform (e.Item);
vargraphics = e.PaintEventArgs.Graphics;
varpt = LeadPointD。创建(0,0);
pt = transform.Transform(pt);
图形。拉带(“加载……”,这.ImageViewer。字体、画笔。黑色(浮动pt。X (浮动) pt.Y);
}
}
最后,在查看器中设置我们的虚拟器
//添加我们的虚拟器
imageview。仿真器=新MyVirtualizer(文件名);
运行这个演示并注意:
查看器立即启动并运行
当滚动和缩放时,会按需调用虚拟器来加载和丢弃页面
的ImageViewerVirtualizer方法将从一个与主UI线程不同的线程中调用。这对于确保用户可以平滑地平移和缩放而不受干扰是很重要的。DeleteItem当项目被删除或查看器被处理时将被调用(如果AutoDisposeImages设置为真正的),以确保资源得以释放。
的RenderItemPlaceholder方法总是在创建查看器的同一线程中调用。