C#实现长图拖拽显示功能

  完整demo程序可以去Github下载,点击链接跳转

1. 实现原理

  Winform实现长图显示控件。原理比较简单,会用到3个PictureBox控件,紧密排列,令其中的一部分显示到界面上,加上鼠标滑动事件,可以使得图片发生移动,判断达到某个条件的时候,将最左边或者最右边的PictureBox控件移动到新的位置,即可实现拖拽长图显示。

  那么达到什么条件时才去变更PictureBox的排列位置?我们可以加一条辅助线辅助线在显示区域内时,就是正常显示;一旦辅助线脱离显示区域,那么就变更PictureBox的排列位置,使辅助线重新回到显示区域内。

  为达到更直观的效果,以下图为例,每个图片代表一个PictureBox,上面写的数字代表顺序,两条红线之间代表【显示的区域】,两条红线之外认为是不显示,黑线代表判断移动的条件,也就是上文提到的【辅助线】,当【辅助线】离开两条红线范围内的区域时,移动PictureBox,可以显示一张新的图片移动到移动方向的末尾。实现了资源的可重复利用。(思想上有点类似于Android的ListView组件扩展链接

2022-08-19 16-46-59 00_00_00-00_00_30

  配合一个裁图的程序,可以实现拖拽长图的显示。(同样认为两条红线之间是显示的区域,那么这个区域是可以完整显示整个长图的)

2022-08-19 17-03-40 00_00_00-00_00_30

  正式使用示意图

2022-08-19 17-13-53 00_00_00-00_00_30

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控件

image-20220819171717998

  调用示例:

//传入的参数是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控件的显示方式,比如拉伸,或者是成比例显示等等。

image-20220819172943901

  完整demo程序可以去Github下载,点击链接跳转

如果觉得文章对你有帮助的话,留个赞再走吧,非常感谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纸照片

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值