在现代的触摸设备上,用户常常需要查看比屏幕更大的图片。传统的滚动条在这种交互方式下并不总是那么方便,尤其是在“触摸”体验日益流行的今天。因此,决定开发一个新的控件,让用户能够通过手指来浏览图片。之后,添加了缩放功能,让用户能够轻松地查看整张图片,然后进行导航。
源代码项目将构建一个DLL文件,包含该控件。然后,可以将其添加到Visual Studio工具箱中,并像使用普通的PictureBox一样使用它。如果打开包含在zip文件中的解决方案,构建它并启动项目,将得到一个示例应用程序,可以将其上传到Pocket PC设备上进行实时测试!使用底部的“实时演示”菜单,使用嵌入的图片(这是标准的Windows Vista壁纸)测试控件,或者可以浏览设备上的其他图片,然后点击“显示”按钮,使用自己的图片测试控件!
以下是一些关键的控件属性:
现在,来看看最重要的代码部分:三个事件,它们是实现功能的核心。这些是OnMouseDown、OnMouseMove和OnPaint。事实上,想要获取用户在屏幕上点击的时刻并保持坐标。然后,当用户在保持手写笔或手指压力的同时移动时,想要滚动图片。
有一个初步的解决方案:记住第一次点击的坐标,然后总是使用这个记忆和实际的MouseMove坐标来计算新的图片坐标。但这有点复杂。所以使用的解决方案将在MouseMove上计算新的图片坐标,然后将新的鼠标(手写笔或手指)坐标作为“第一个”坐标保存。
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// 左键点击,保存点击位置
_iFirstTapX = e.X;
_iFirstTapY = e.Y;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// 移动CropLocation的大小
_pCropLocation.X -= (e.X - _iFirstTapX);
_pCropLocation.Y -= (e.Y - _iFirstTapY);
// 替换图片以避免超出边缘
Replace();
// 图片移动了,保存新的点击位置以供下次移动
_iFirstTapX = e.X;
_iFirstTapY = e.Y;
this.Invalidate();
}
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics gxOff;
// 离屏图形
if (_bOffscreen == null)
// 双缓冲的Bitmap
_bOffscreen = new Bitmap(ClientSize.Width, ClientSize.Height);
gxOff = Graphics.FromImage(_bOffscreen);
gxOff.Clear(this.BackColor);
if (_bPicture != null)
{
Rectangle rDst = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height);
Rectangle rSrc = new Rectangle(_pCropLocation.X, _pCropLocation.Y, ClientSize.Width, ClientSize.Height);
gxOff.DrawImage(_bZoomedPicture, rDst, rSrc, GraphicsUnit.Pixel);
}
// 从内存Bitmap绘制
e.Graphics.DrawImage(_bOffscreen, 0, 0);
base.OnPaint(e);
}
_iFirstTapX和_iFirstTapY将保持第一次点击的坐标。_pCropLocation是一个Point,定义了应该显示的图片部分的左上角坐标。称之为整个图片的裁剪。然后会看到一个调用Replace()方法。这将检查位移是否超出了图片的边缘,如果StopEdges属性设置为true,它将把它放回。this.Invalidate()总是在控件需要重绘时调用。所以在这里来到了OnPaint事件。正如看到的,它将使用双缓冲来防止闪烁。创建了一个临时的Bitmap,大小与控件相同,然后看看是否设置了图片。如果true,获取原始图片需要显示的矩形部分,并将其绘制到临时Bitmap上。
但等一下!这个_bZoomedPicture是什么?好吧,为了在缩放模式下允许轻松滑动,控件在内存中创建了缩放的图片,这将是绘制的源图片。所以当更改Picture属性、Zoom属性,或者使用ZoomIn()或ZoomOut()方法时,将调用CreateZoomedPicture()来计算这个图片的新版本。以下是代码:
private void CreateZoomedPicture()
{
// 绘制图片的缩放版本。
// 实际上这个版本将用于控件绘制。
if (_bPicture != null)
{
if (_iZoom == 100)
_bZoomedPicture = _bPicture;
else
{
Graphics gx;
_bZoomedPicture = new Bitmap((int)((double)_bPicture.Width * _dZoom), (int)((double)_bPicture.Height * _dZoom));
gx = Graphics.FromImage(_bZoomedPicture);
gx.Clear(this.BackColor);
Rectangle rDst = new Rectangle(0, 0, _bZoomedPicture.Width, _bZoomedPicture.Height);
Rectangle rSrc = new Rectangle(0, 0, _bPicture.Width, _bPicture.Height);
gx.DrawImage(_bPicture, rDst, rSrc, GraphicsUnit.Pixel);
}
}
}
在这里,只是利用了.NET Compact Framework的功能:在更大或更小的矩形中绘制源Bitmap。图片将被重新采样,而不必担心如何做到这一点!
可以在演示项目中看到,可以制作一个Windows表单,用这个控件填充,并且为设备的两个软按钮定义了Zoom In和Zoom Out两个菜单,然后进行全屏导航。每个按钮的代码将简单地调用touchPictureBox1.ZoomIn()和touchPictureBox1.ZoomOut()!但也可以将它作为一个小控件在表单中使用,进行小范围的导航。
包括将得到一个Visual Studio 2008项目,但它应该很容易在Visual Studio 2005中工作,因为它使用的是.NET CF 2.0。还将得到一个小图标,以便在工具箱中有很好的表示,以及DesignTimeAttributes.xmta文件,以便有良好的属性行为和描述!