- 【摘要】 本文描述了如何使用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、鼠标按住拖动图像。
-
预览
-
代码
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] 社区,共同学习交流!