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