C#自定义控件-实现了一个支持平移、缩放、双击重置的图像显示控件

1. 控件概述

这是一个继承自 Control 的自定义控件,主要用于图像的显示和交互操作,具有以下核心功能:

  • 图像显示与缩放(支持鼠标滚轮缩放)
  • 图像平移(支持鼠标拖拽)
  • 视图重置(双击重置)
  • 网格背景显示

2. 核心功能实现分析

2.1 图像渲染与缩放
  • 使用 InterpolationMode.HighQualityBicubic 和 PixelOffsetMode.HighQuality 实现高质量图像渲染
  • 通过 _zoom 变量控制缩放比例,_imagePosition 控制图像位置
  • 坐标转换函数 ScreenToImage 用于将屏幕坐标映射到图像坐标
private PointF ScreenToImage(Point screenPoint)
{
    return new PointF((screenPoint.X - _imagePosition.X) / _zoom, 
                      (screenPoint.Y - _imagePosition.Y) / _zoom);
}
2.2 视图控制
  • ResetView() 方法自动计算适合控件大小的缩放比例
  • CenterImage() 方法确保图像居中显示
  • 实现鼠标事件处理:
    • 左键拖拽实现图像平移
    • 滚轮实现缩放(缩放时保持鼠标位置不变)
    • 双击重置视图
protected override void OnMouseWheel(MouseEventArgs e)
{
    // 缩放前记录鼠标在图像中的位置
    PointF mouseInImageSpace = ScreenToImage(e.Location);
    
    // 调整缩放比例
    _zoom += (e.Delta > 0 ? ZoomStep : -ZoomStep);
    _zoom = Math.Max(MinZoom, Math.Min(MaxZoom, _zoom));
    
    // 计算缩放后的位置并调整图像位置,确保鼠标点在图像中的相对位置不变
    PointF mouseInScreenSpaceAfterZoom = new PointF(
        mouseInImageSpace.X * _zoom + _imagePosition.X,
        mouseInImageSpace.Y * _zoom + _imagePosition.Y
    );
    
    _imagePosition.X += e.Location.X - mouseInScreenSpaceAfterZoom.X;
    _imagePosition.Y += e.Location.Y - mouseInScreenSpaceAfterZoom.Y;
    Invalidate();
}
2.3 网格背景实现
  • 通过 ShowGrid 属性控制网格显示
  • 在 OnPaint 方法中绘制网格背景
  • 网格参数可配置(颜色、大小)
// 绘制网格背景
if (_showGrid)
{
    using (Pen gridPen = new Pen(_gridColor))
    {
        // 水平线
        for (float y = 0; y < Height; y += GridSize)
        {
            e.Graphics.DrawLine(gridPen, 0, y, Width, y);
        }
        // 垂直线
        for (float x = 0; x < Width; x += GridSize)
        {
            e.Graphics.DrawLine(gridPen, x, 0, x, Height);
        }
    }
}

3. 使用示例 

// 在窗体中使用自定义控件
public partial class MainForm : Form
{
    private CustomControl1 imageControl;
    
    public MainForm()
    {
        InitializeComponent();
        
        // 创建自定义控件实例
        imageControl = new CustomControl1();
        imageControl.Dock = DockStyle.Fill;
        this.Controls.Add(imageControl);
        
        // 加载图像
        try
        {
            imageControl.Image = Image.FromFile("example.jpg");
        }
        catch (Exception ex)
        {
            MessageBox.Show($"加载图像失败: {ex.Message}");
        }
    }
}

完整代码:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace ImageControl
{
    public partial class CustomControl1 : Control
    {
        private Image _image;
        private Point _lastMousePosition;
        private PointF _imagePosition = new PointF(0, 0);
        private float _zoom = 1.0f;
        private const float MinZoom = 0.2f;
        private const float MaxZoom = 5.0f;
        private const float ZoomStep = 0.1f;
        private bool _showGrid = true;  // 新增:控制网格显示
        private readonly Color _gridColor = Color.LightGray;  // 新增:网格颜色
        private const int GridSize = 20;  // 新增:网格大小

        public Image Image
        {
            get => _image;
            set
            {
                if (_image != null && _image != value)
                {
                    _image.Dispose();
                }
                _image = value;

                // 自动调整初始缩放比例
                if (_image != null && this.Width > 0 && this.Height > 0)
                {
                    float widthRatio = (float)this.Width / _image.Width;
                    float heightRatio = (float)this.Height / _image.Height;
                    _zoom = Math.Min(widthRatio, heightRatio) * 0.95f;
                    _zoom = Math.Max(MinZoom, Math.Min(MaxZoom, _zoom));
                }

                CenterImage();
                Invalidate();
            }
        }

        // 新增:网格显示属性
        public bool ShowGrid
        {
            get => _showGrid;
            set
            {
                if (_showGrid != value)
                {
                    _showGrid = value;
                    Invalidate();
                }
            }
        }

        public CustomControl1()
        {
            this.DoubleBuffered = true;
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
        }

        private PointF ScreenToImage(Point screenPoint)
        {
            return new PointF((screenPoint.X - _imagePosition.X) / _zoom, (screenPoint.Y - _imagePosition.Y) / _zoom);
        }

        private RectangleF GetDestinationRectangle()
        {
            if (_image == null) return RectangleF.Empty;
            return new RectangleF(_imagePosition.X, _imagePosition.Y, (_image.Width * _zoom), (_image.Height * _zoom));
        }

        private void CenterImage()
        {
            if (_image == null) return;

            _imagePosition = new PointF(
                (this.Width - _image.Width * _zoom) / 2,
                (this.Height - _image.Height * _zoom) / 2);

            Invalidate();
        }

        public void ResetView()
        {
            if (_image == null)
            {
                _zoom = 1.0f;
            }
            else
            {
                // 计算适合控件大小的缩放比例
                float widthRatio = (float)this.Width / _image.Width;
                float heightRatio = (float)this.Height / _image.Height;
                _zoom = Math.Min(widthRatio, heightRatio) * 0.95f;
                _zoom = Math.Max(MinZoom, Math.Min(MaxZoom, _zoom));
            }

            CenterImage();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            // 新增:绘制网格背景
            if (_showGrid)
            {
                using (Pen gridPen = new Pen(_gridColor))
                {
                    // 水平线
                    for (float y = 0; y < Height; y += GridSize)
                    {
                        e.Graphics.DrawLine(gridPen, 0, y, Width, y);
                    }
                    // 垂直线
                    for (float x = 0; x < Width; x += GridSize)
                    {
                        e.Graphics.DrawLine(gridPen, x, 0, x, Height);
                    }
                }
            }

            if (_image == null) return;

            e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            RectangleF destRect = GetDestinationRectangle();
            e.Graphics.DrawImage(_image, destRect);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            CenterImage();
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);
            ResetView();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Button == MouseButtons.Left && _image != null)
            {
                _lastMousePosition = e.Location;
                this.Cursor = Cursors.Hand;
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            this.Cursor = Cursors.Default;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == MouseButtons.Left && _image != null)
            {
                _imagePosition.X += (e.X - _lastMousePosition.X);
                _imagePosition.Y += (e.Y - _lastMousePosition.Y);
                _lastMousePosition = e.Location;
                Invalidate();
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (_image == null) return;

            PointF mouseInImageSpace = ScreenToImage(e.Location);
            float oldZoom = _zoom;
            _zoom += (e.Delta > 0 ? ZoomStep : -ZoomStep);
            _zoom = Math.Max(MinZoom, Math.Min(MaxZoom, _zoom));

            PointF mouseInScreenSpaceAfterZoom = new PointF(
                mouseInImageSpace.X * _zoom + _imagePosition.X,
                mouseInImageSpace.Y * _zoom + _imagePosition.Y
            );

            _imagePosition.X += e.Location.X - mouseInScreenSpaceAfterZoom.X;
            _imagePosition.Y += e.Location.Y - mouseInScreenSpaceAfterZoom.Y;
            Invalidate();
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值