【C#】Winform自定义图像控件实现滚动缩放效果、拖动显示图像。

  • 【摘要】 本文描述了如何使用Winform创建用户图像控件、实现滚动缩放效果,拖动显示图像。

  • 前言

    • 1、PictureBox

      • PictureBox 是 Windows Forms (WinForms) 中的一个内置控件,专门用于在窗体上显示图像,其可以显示多种格式的图像(如:BMP、JPEG、GIF、PNG、ICO 等)。
    • 2、PictureBox 内置了如下几种图像显示方式:

      • PictureBox:内置了如下几种图像显示方式:
      • Normal:图像放置在控件的左上角,不缩放。
      • StretchImage:拉伸图像以填充控件。
      • AutoSize:调整控件大小以适应图像。
      • CenterImage:图像居中显示,不缩放。
      • Zoom:按比例缩放图像,保持纵横比。
    • 上面的图像显示方法只能实现基本的图像功能。如果想要实现更好的体验效果。

    • 还需要自定义一些功能,下面就是自定义一个图像用户控件实现滚动鼠标缩放图像、鼠标拖动显示图像。


  • 运行环境

    • 系 统:Win11
    • 开发工具:Visual Studio 2022
    • .Net版本:.Net Framework 4.6.0
  • 实现功能

    • 1、鼠标双击还原。
    • 2、鼠标滚动缩放:放大、缩小。
    • 3、点击按钮:放大、缩小、还原 。
    • 4、鼠标按住拖动图像。
  • 预览

    • 运行效果

在这里插入图片描述
在这里插入图片描述

  • 代码

    • UCPictrueBox

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace CustomControls
    {
        public partial class UCPictrueBox : UserControl
        {
            #region 字段、属性
    
            #region 字段
            private Image _image;                           //图像
            private float _zoomFactor = 1.0f;               //缩放因子
            private const float ZoomIncrement = 0.1f;       //缩放增量
            private const float MinZoom = 0.01f;            //最小缩放
            private const float MaxZoom = 20.0f;            //最大缩放
    
            private Point _lastLocation;                    //最后位置
            private bool _isDragging = false;               //是否拖拽
            private PointF _imagePosition = PointF.Empty;   //图像位置
            #endregion
    
            #region 属性
             [Description("获取或设置控件显示的图像。")]
            [Category("UserDefine")]
            public Image Image
            {
                get => _image;
                set
                {
                    _image = value;
                    _zoomFactor = 1.0f;
                    _imagePosition = PointF.Empty;
                    ImageCenter();
                    Invalidate();
                }
            }
            #endregion
            #endregion
    
            #region 构造函数
            public UCPictrueBox()
            {
                InitializeComponent();
                this.DoubleBuffered = true;
                this.BackColor = Color.LightGray;
                this.BorderStyle = BorderStyle.FixedSingle;
                this.MouseWheel += ImageZoom_MouseWheel;
                this.MouseDown += ImageZoom_MouseDown;
                this.MouseMove += ImageZoom_MouseMove;
                this.MouseUp += ImageZoom_MouseUp;
                this.MouseDoubleClick += ImageZoom_MouseDoubleClick;
            }
            #endregion
    
            #region 图像操作
            /// <summary>
            /// 图像居中:使图像在水平和垂直方向上都居中
            /// (X,Y) = ( (workWidth - ScaledWidth)/2, (workHeight - ScaledHeight)/2)
            /// </summary>
            private void ImageCenter()
            {
                if (_image == null) return;
                Size scaledSize = GetScaledSize();
                _imagePosition = new PointF((this.ClientSize.Width - scaledSize.Width) / 2f, (this.ClientSize.Height - scaledSize.Height) / 2f);
                Invalidate();
            }
            /// <summary>
            /// 图像缩放:基于鼠标位置的缩放
            /// </summary>
            private void Zoom(float factor)
            {
                if (_image == null) return;
                //获取鼠标光标位置 转换为 工作区坐标。
                Point mousePos = this.PointToClient(MousePosition);
                Size scaledSize = GetScaledSize();
                float mouseXRelative = (mousePos.X - _imagePosition.X) / scaledSize.Width;
                float mouseYRelative = (mousePos.Y - _imagePosition.Y) / scaledSize.Height;
    
                // 新缩放因子 = 当前缩放因子 + 传入的缩放增量
                float newZoom = _zoomFactor + factor;
                // 使用Math.Max和Math.Min确保缩放因子在最小(MinZoom)和最大(MaxZoom)限制之间
                newZoom = Math.Max(MinZoom, Math.Min(MaxZoom, newZoom));
                //如果没有显著变化,则不执行任何操作,条件:(缩放因子> 0.01)
                if (Math.Abs(newZoom - _zoomFactor) > 0.01f)
                {
                    _zoomFactor = newZoom;
                    Size newSize = GetScaledSize();
                    //调整图像位置:(X,Y) = ( (workWidth - ScaledWidth)/2, (workHeight - ScaledHeight)/2)
                    _imagePosition = new PointF(mousePos.X - mouseXRelative * newSize.Width, mousePos.Y - mouseYRelative * newSize.Height);
                    //如果缩放后的图像完全适合工作区(小于等于工作区尺寸),则调用ImageCenter()使其居中
                    if (newSize.Width <= this.ClientSize.Width && newSize.Height <= this.ClientSize.Height)
                    {
                        ImageCenter();
                    }
                    Invalidate();
                }
            }
            /// <summary>
            /// 放大
            /// </summary>
            public void ZoomIn()
            {
                Zoom(ZoomIncrement);
            }
            /// <summary>
            /// 缩小
            /// </summary>
            public void ZoomOut()
            {
                Zoom(-ZoomIncrement);
            }
            /// <summary>
            /// 还原
            /// </summary>
            public void ZoomReset()
            {
                _zoomFactor = 1.0f;
                ImageCenter();
            }
            /// <summary>
            ///  计算图像缩放尺寸:(newW ,newH) =(width ,height)* zoomFactor
            /// </summary>
            private Size GetScaledSize()
            {
                return new Size((int)(_image.Width * _zoomFactor), (int)(_image.Height * _zoomFactor));
            }
            #endregion
    
            #region 事件方法重写
            /// <summary>
            /// 重绘
            /// </summary>
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
    
                if (_image == null) return;
                // 计算缩放后的图像尺寸
                Size scaleSize = GetScaledSize();
                // 计算绘制位置(确保居中)
                Rectangle destRect;
                if (_imagePosition == PointF.Empty)
                {
                    // 初始居中显示
                    int x = (this.ClientSize.Width - scaleSize.Width) / 2;
                    int y = (this.ClientSize.Height - scaleSize.Height) / 2;
                    destRect = new Rectangle(x, y, scaleSize.Width, scaleSize.Height);
                }
                else
                {
                    // 拖拽后的位置
                    destRect = new Rectangle(
                        (int)_imagePosition.X,
                        (int)_imagePosition.Y,
                        scaleSize.Width,
                        scaleSize.Height);
                }
                // 绘制图像
                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                e.Graphics.DrawImage(_image, destRect);
            }
            /// <summary>
            /// 重设大小
            /// </summary>
            protected override void OnResize(EventArgs e)
            {
                base.OnResize(e);
                if (_image != null)
                {
                    // 如果图像比控件小,则居中显示
                    Size scaledSize = GetScaledSize();
                    if (scaledSize.Width <= this.ClientSize.Width && scaledSize.Height <= this.ClientSize.Height)
                    {
                        ImageCenter();
                    }
                }
            }
            #endregion
    
            #region 鼠标事件处理:滚轮缩放、按下拖拽、移动显示、松开取消拖拽、双击还原
            private void ImageZoom_MouseWheel(object sender, MouseEventArgs e)
            {
                if (e.Delta > 0)
                {
                    ZoomIn();
                }
                else if (e.Delta < 0)
                {
                    ZoomOut();
                }
            }
            private void ImageZoom_MouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left && _image != null)
                {
                    _lastLocation = e.Location;
                    _isDragging = true;
                    this.Cursor = Cursors.Hand;
                }
            }
            private void ImageZoom_MouseMove(object sender, MouseEventArgs e)
            {
                if (_isDragging && _image != null)
                {
                    int deltaX = e.X - _lastLocation.X;
                    int deltaY = e.Y - _lastLocation.Y;
                    _imagePosition.X += deltaX;
                    _imagePosition.Y += deltaY;
                    _lastLocation = e.Location;
                    Invalidate();
                }
            }
            private void ImageZoom_MouseUp(object sender, MouseEventArgs e)
            {
                _isDragging = false;
                this.Cursor = Cursors.Default;
            }
            private void ImageZoom_MouseDoubleClick(object sender, MouseEventArgs e)
            {
                ZoomReset();
            }
            #endregion
        }
    }
    
  • CustomControls

    using System.ComponentModel;
    using System;
    using System.Windows.Forms;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    namespace CustomControls
    {
        public class UCButton : Button
        {
            #region  公共字段、属性
            #region 按钮样式属性
            private Color _defaultColor;
            private bool _selectedState = false;
            [Category("UserProperty")]
            [Description("选中状态")]
            public bool SelectedState
            {
                get => _selectedState;
                private set
                {
                    _selectedState = value;
                    this.Invalidate();
                }
            }
    
            private int radius = 15;
            [Category("UserProperty")]
            [Description("圆角半径")]
            public int Radius
            {
                get { return radius; }
                set
                {
                    if (radius != value)
                    {
                        radius = value;
                        this.Invalidate();
                    }
    
                }
            }
    
    
            private Color _hoverColor = Color.LightBlue;
            [Category("UserProperty")]
            [Description("鼠标悬停时的背景色")]
            public Color HoverColor
            {
                get => _hoverColor;
                set
                {
                    if (value != _hoverColor)
                    {
                        _hoverColor = value;
                        this.Invalidate();
                    }
    
                }
            }
            #endregion
    
            #region 按钮提示
            private ToolTip toolTip = new ToolTip();
            private string toolTipText = string.Empty;
    
            [Category("UserProperty")]
            [Description("鼠标悬停的提示")]
            public string ToolTipText
            {
                get => toolTipText;
                set
                {
                    if (value != toolTipText)
                    {
                        toolTipText = value;
                        this.Invalidate();
                    }
                }
            }
            #endregion
    
            #endregion
            public UCButton()
            {
                Initialize();
            }
            private void Initialize()
            {
                this.DoubleBuffered = true;
                this.FlatStyle = FlatStyle.Flat;
                this.Size = new Size(100, 30);
                this.FlatAppearance.BorderSize = 0;
                this.SetStyle(ControlStyles.UserPaint
                    | ControlStyles.AllPaintingInWmPaint
                    | ControlStyles.OptimizedDoubleBuffer
                    | ControlStyles.ResizeRedraw
                    | ControlStyles.SupportsTransparentBackColor, true);
                _defaultColor = BackColor;
    
                this.MouseEnter += UCButton_MouseEnter;
                this.MouseLeave += UCButton_MouseLeave;
                UpdataTip();
            }
            private void UpdataTip()
            {
                toolTip.ForeColor = Color.White;        // 文字颜色
                toolTip.BackColor = Color.Blue;         // 背景颜色
                toolTip.AutoPopDelay = 1000;             // 提示显示持续时间(毫秒)
                toolTip.InitialDelay = 1000;            // 鼠标悬停后显示提示的延迟时间
                toolTip.ReshowDelay = 1000;             // 从一个控件移动到另一个控件时显示提示的延迟时间
            }
            private void UCButton_MouseEnter(object sender, EventArgs e)
            {
                this.BackColor = HoverColor; // 鼠标进入时更改背景色
                toolTip.SetToolTip(this, toolTipText);
            }
            private void UCButton_MouseLeave(object sender, EventArgs e)
            {
                this.BackColor = _defaultColor; // 鼠标离开时恢复默认背景色
            }
            protected override void OnClick(EventArgs e)
            {
                base.OnClick(e);
                _selectedState = !_selectedState;
            }
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                // 设置抗锯齿
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                // 高质量合成
                e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
                // 高质量插值
                e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                // 绘制圆角矩形:左上角、右上角、右下角、左下角
                using (GraphicsPath path = new GraphicsPath())
                {
                    path.AddArc(0, 0, radius, radius, 180, 90);
                    path.AddArc(this.Width - radius, 0, radius, radius, 270, 90);
                    path.AddArc(this.Width - radius, this.Height - radius, radius, radius, 0, 90);
                    path.AddArc(0, this.Height - radius, radius, radius, 90, 90);
                    path.CloseFigure();
                    // 设置按钮的区域为圆角矩形
                    this.Region = new Region(path); 
                }
            }
        }
    }
    
    
  • MainForm

    public partial class MainForm : WinFormBase
    {
        public MainForm()
        {
            InitializeComponent();
            this.CenterToParent();
            this.CenterToScreen();
        }
        private void btn_ZoomIn_Click(object sender, System.EventArgs e)
        {
            ucPic_Image.ZoomIn();
        }
        private void btn_ZoomOut_Click(object sender, System.EventArgs e)
        {
            ucPic_Image.ZoomOut();
        }
        private void btn_Reset_Click(object sender, System.EventArgs e)
        {
            string filePath = Application.StartupPath + "\\source.png";
            ucPic_Image.Image = Image.FromFile(filePath);
        }
    }
    
  • 结语

    • 使用方法,将自定义控件创建在同一个命名空间下,点击生成无报错后,即可在工具箱中查看选择自定义控件。拖拽到当前窗体即可。
    • 如果是创建自己的类库,并引用这个类库,引用时可能得报错原因,目标框架不同。
    • 公众号:编程笔记in
  • 最后

    • 如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!
    • 如有疑问或需要进一步的帮助,欢迎评论区留言。
    • 也可以加入微信公众号 [编程笔记in] 社区,共同学习交流!
Vivado2023是一款集成开发环境软件,用于设计和验证FPGA(现场可编程门阵列)和可编程逻辑器件。对于使用Vivado2023的用户来说,license是必不可少的。 Vivado2023的license是一种许可证,用于授权用户合法使用该软件。许可证分为多种类型,包括评估许可证、开发许可证和节点许可证等。每种许可证都有不同的使用条件和功能。 评估许可证是免费提供的,让用户可以在一段时间内试用Vivado2023的全部功能。用户可以使用这个许可证来了解软件的性能和特点,对于初学者和小规模项目来说是一个很好的选择。但是,使用评估许可证的用户在使用期限过后需要购买正式的许可证才能继续使用软件。 开发许可证是付费的,可以永久使用Vivado2023的全部功能。这种许可证适用于需要长期使用Vivado2023进行开发的用户,通常是专业的FPGA设计师或工程师。购买开发许可证可以享受Vivado2023的技术支持和更新服务,确保软件始终保持最新的版本和功能。 节点许可证是用于多设备或分布式设计的许可证,可以在多个计算机上安装Vivado2023,并共享使用。节点许可证适用于大规模项目或需要多个处理节点进行设计的用户,可以提高工作效率和资源利用率。 总之,Vivado2023 license是用户在使用Vivado2023时必须考虑的问题。用户可以根据自己的需求选择合适的许可证类型,以便获取最佳的软件使用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程笔记in

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

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

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

打赏作者

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

抵扣说明:

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

余额充值