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();
}
}
}