完整demo程序可以去Github下载,点击链接跳转
1. 实现原理
Winform实现长图显示控件。原理比较简单,会用到3个PictureBox控件,紧密排列,令其中的一部分显示到界面上,加上鼠标滑动事件,可以使得图片发生移动,判断达到某个条件的时候,将最左边或者最右边的PictureBox控件移动到新的位置,即可实现拖拽长图显示。
那么达到什么条件时才去变更PictureBox的排列位置?我们可以加一条辅助线,辅助线在显示区域内时,就是正常显示;一旦辅助线脱离显示区域,那么就变更PictureBox的排列位置,使辅助线重新回到显示区域内。
为达到更直观的效果,以下图为例,每个图片代表一个PictureBox,上面写的数字代表顺序,两条红线之间代表【显示的区域】,两条红线之外认为是不显示,黑线代表判断移动的条件,也就是上文提到的【辅助线】,当【辅助线】离开两条红线范围内的区域时,移动PictureBox,可以显示一张新的图片移动到移动方向的末尾。实现了资源的可重复利用。(思想上有点类似于Android的ListView组件扩展链接)
配合一个裁图的程序,可以实现拖拽长图的显示。(同样认为两条红线之间是显示的区域,那么这个区域是可以完整显示整个长图的)
正式使用示意图
2. 程序介绍
Talk is cheap. Show me the code
裁剪图像使用的辅助方法
/// <summary>
/// 裁剪图像
/// </summary>
/// <param name="imagePath">图像地址</param>
/// <param name="pointX">起始点x</param>
/// <param name="pointY">起始点y</param>
/// <param name="width">图像宽度</param>
/// <param name="height">图像高度</param>
/// <returns>裁剪好的图像</returns>
private Image TailorImage(string imagePath, int pointX, int pointY, int width, int height)
{
Image originImage = Image.FromFile(imagePath);
if (pointX < 0 || pointX > originImage.Width)
{
return null;
}
Rectangle cropRegion = new Rectangle(pointX, pointY, width, height);
Bitmap result = new Bitmap(cropRegion.Width, cropRegion.Height);
Graphics graphics = Graphics.FromImage(result);
graphics.DrawImage(originImage, new Rectangle(0, 0, cropRegion.Width, cropRegion.Height), cropRegion, GraphicsUnit.Pixel);
return result;
}
初始定义3个picturebox,存储到数组内便于处理逻辑
//声明三个PictureBox控件
PictureBox pictureBox1 = new PictureBox();
PictureBox pictureBox2 = new PictureBox();
PictureBox pictureBox3 = new PictureBox();
//存到数组里
Boxes = new PictureBox[] { pictureBox1, pictureBox2, pictureBox3 };
需要处理三个事件。1. 鼠标按下;2.鼠标移动;3.鼠标释放。鼠标按下时,需要记录各个PictureBox当前的坐标信息和辅助线当前的坐标信息,便于之后比较,同时也是开启鼠标移动计算的信号;2.鼠标释放时停止计算鼠标移动;3.鼠标移动时需要移动这三个PictureBox和辅助线,当辅助线移动到显示区域之外时,重新排列PictureBox位置,同时加载新的图像。
/// <summary>
/// 为PictureBox绑定事件
/// </summary>
private void BoxesBindEvent()
{
for (int index = 0; index < Boxes.Length; index++)
{
Boxes[index].Width = this.Width;
Boxes[index].Height = this.Height;
Boxes[index].SizeMode = PictureBoxSizeMode.StretchImage;
this.Controls.Add(Boxes[index]);
Boxes[index].MouseDown += new System.Windows.Forms.MouseEventHandler((object sender, MouseEventArgs e) => { //鼠标点击事件
MouseDownCalculate();
});
Boxes[index].MouseMove += new System.Windows.Forms.MouseEventHandler((object sender, MouseEventArgs e) => { //鼠标移动事件
MouseMoveCalculate();
});
Boxes[index].MouseUp += new System.Windows.Forms.MouseEventHandler((object sender, MouseEventArgs e) => { //鼠标释放事件
isMouseDown = false;
});
}
}
/// <summary>
/// 鼠标按下时的处理
/// </summary>
private void MouseDownCalculate()
{
isMouseDown = true;
MousePoint = PointToClient(Control.MousePosition);//记录鼠标坐标
Pbx2Point = Boxes[BoxIndex2].Location;
Pbx1Point = Boxes[BoxIndex1].Location;
Pbx3Point = Boxes[BoxIndex3].Location;
HalfPoint = new Point(Boxes[BoxIndex2].Location.X + (Boxes[BoxIndex2].Width / 2), 0);
}
/// <summary>
/// 鼠标移动时的处理
/// </summary>
private void MouseMoveCalculate()
{
//鼠标坐标的相对改变值
int a = PointToClient(Control.MousePosition).X - MousePoint.X;
int b = PointToClient(Control.MousePosition).Y - MousePoint.Y;
//图片坐标计算&赋值
if (isMouseDown)
{
Boxes[BoxIndex2].Location = new Point(Pbx2Point.X + a, Pbx2Point.Y);//图片新的坐标 = 图片起始坐标 + 鼠标相对位移
Boxes[BoxIndex1].Location = new Point(Pbx1Point.X + a, Pbx1Point.Y);
Boxes[BoxIndex3].Location = new Point(Pbx3Point.X + a, Pbx3Point.Y);
TagLine = new Point(HalfPoint.X + a, HalfPoint.Y);
if (TagLine.X + Offset < this.Location.X)
{
Boxes[BoxIndex1].Left = Boxes[BoxIndex3].Right - Offset;
LeftBorder += this.ImageWidth;
Boxes[BoxIndex1].Image = TailorImage(this.FilePath, RightBorder, 0, this.ImageWidth, this.ImageHeight);
RightBorder += this.ImageWidth;
BoxIndex1 = Add(BoxIndex1);
BoxIndex2 = Add(BoxIndex2);
BoxIndex3 = Add(BoxIndex3);
MouseDownCalculate();
HalfPoint = new Point(Boxes[BoxIndex2].Location.X + (Boxes[BoxIndex2].Width / 2), 0);
//Debug.Print("偏移后:" + BoxIndex1.ToString());
}
if (TagLine.X - Offset > (this.Location.X + this.Width))
{
Boxes[BoxIndex3].Left = Boxes[BoxIndex1].Left - Boxes[BoxIndex1].Width + Offset;
LeftBorder -= this.ImageWidth;
RightBorder -= this.ImageWidth;
Boxes[BoxIndex3].Image = TailorImage(this.FilePath, LeftBorder, 0, this.ImageWidth, this.ImageHeight);
BoxIndex1 = Add(BoxIndex1, 2);
BoxIndex2 = Add(BoxIndex2, 2);
BoxIndex3 = Add(BoxIndex3, 2);
MouseDownCalculate();
HalfPoint = new Point(Boxes[BoxIndex2].Location.X + (Boxes[BoxIndex2].Width / 2), 0);
}
}
}
3. 直接调用
为方便使用,我把功能封成了一个UserControl控件,直接调用即可,调用时需要在Form界面上放置一个Panel控件
调用示例:
//传入的参数是Panel控件的宽和高
UCShowLongPicture longPicture = new UCShowLongPicture(MainPanel.Width, MainPanel.Height);
//参数1是图像在文件内的地址,参数2是显示出来的图像的宽度,参数3是显示出来的图像的高度
longPicture.SetImageParameter(@"C:\Users\admin\Pictures\Saved Pictures\test.jpg", 400, 300);
//将UserControl控件显示到Panel控件上
MainPanel.Controls.Add(ShowLongPictureUtil);
至于为什么传一组Panel控件的宽和高,还要再传一组图像的宽和高?
- Panel控件的宽和高表示的是显示区域的大小;而图像的宽和高是裁图的依据,表示一次性显示多大的内容。
另外,根据自己的需求,修改UserControl里的PictureBox控件的显示方式,比如拉伸,或者是成比例显示等等。
完整demo程序可以去Github下载,点击链接跳转
如果觉得文章对你有帮助的话,留个赞再走吧,非常感谢!!!