using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace DHSoftware.Views { public partial class ImageViewerControl : UserControl { #region 内部控件 private PictureBox pictureBox; private Label statusLabel; #endregion #region 核心字段 private Bitmap _currentImage; private float _scale = 1.0f; private PointF _offset = PointF.Empty; private Point _dragStart; private bool _isDragging; private readonly object _imageLock = new object(); #endregion #region 公开属性 public Bitmap Image { get { lock (_imageLock) { return _currentImage?.Clone() as Bitmap; } } set { Bitmap newImage = value?.Clone() as Bitmap; Bitmap oldImageToDispose = null; lock (_imageLock) { // 交换图像引用 oldImageToDispose = _currentImage; _currentImage = newImage; if (_currentImage != null) { if (oldImageToDispose?.Size != _currentImage.Size) { AutoFit(); } else { ClampOffset(); } } } // 在锁外安全释放旧图像 if (oldImageToDispose != null) { // 使用BeginInvoke确保在UI线程释放资源 BeginInvoke(new Action(() => { oldImageToDispose.Dispose(); oldImageToDispose = null; })); } SafeInvalidate(); } } #endregion public ImageViewerControl() { InitializeComponents(); SetupDoubleBuffering(); } #region 初始化 private void InitializeComponents() { pictureBox = new PictureBox { Dock = DockStyle.Fill, BackColor = Color.DarkGray, Cursor = Cursors.Hand }; statusLabel = new Label { Dock = DockStyle.Bottom, Height = 20, Text = "就绪", BorderStyle = BorderStyle.FixedSingle, Font = new Font("Consolas", 10) }; pictureBox.MouseDown += PictureBox_MouseDown; pictureBox.MouseMove += PictureBox_MouseMove; pictureBox.MouseUp += PictureBox_MouseUp; pictureBox.MouseWheel += PictureBox_MouseWheel; pictureBox.Paint += PictureBox_Paint; Controls.Add(pictureBox); Controls.Add(statusLabel); } private void SetupDoubleBuffering() { typeof(PictureBox).GetMethod("SetStyle", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)? .Invoke(pictureBox, new object[] { ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true }); } #endregion #region 核心功能 private void AutoFit() { lock (_imageLock) { if (_currentImage == null) return; const float marginRatio = 0.1f; float marginWidth = Width * marginRatio; float marginHeight = Height * marginRatio; _scale = Math.Min( (Width - marginWidth * 2) / _currentImage.Width, (Height - marginHeight * 2) / _currentImage.Height ); _offset.X = marginWidth + (Width - marginWidth * 2 - _currentImage.Width * _scale) / 2; _offset.Y = marginHeight + (Height - marginHeight * 2 - _currentImage.Height * _scale) / 2; ClampOffset(); } } private void PictureBox_Paint(object sender, PaintEventArgs e) { Bitmap drawImage = null; RectangleF destRect; float scale; PointF offset; // 创建临时绘图参数 lock (_imageLock) { if (_currentImage == null) return; // 创建绘图副本 drawImage = _currentImage.Clone() as Bitmap; scale = _scale; offset = _offset; destRect = new RectangleF( offset.X, offset.Y, drawImage.Width * scale, drawImage.Height * scale); } try { e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; e.Graphics.DrawImage(drawImage, destRect); } finally { drawImage?.Dispose(); } } #endregion #region 鼠标交互(左键拖动) private void PictureBox_MouseDown(object sender, MouseEventArgs e) { if (InvokeRequired) return; lock (_imageLock) { if (_currentImage == null) return; if (e.Button == MouseButtons.Left) { _dragStart = e.Location; _isDragging = true; } } } private void PictureBox_MouseMove(object sender, MouseEventArgs e) { if (InvokeRequired) return; lock (_imageLock) { if (_currentImage == null) return; if (_isDragging) { _offset.X += e.X - _dragStart.X; _offset.Y += e.Y - _dragStart.Y; _dragStart = e.Location; ClampOffset(); SafeInvalidate(); } UpdateStatus(e.Location); } } private void PictureBox_MouseUp(object sender, MouseEventArgs e) { lock (_imageLock) { _isDragging = false; } } private void PictureBox_MouseWheel(object sender, MouseEventArgs e) { if (InvokeRequired) return; lock (_imageLock) { if (_currentImage == null) return; PointF imgPos = ConvertToImageCoords(e.Location); if (imgPos.X < 0 || imgPos.X > _currentImage.Width || imgPos.Y < 0 || imgPos.Y > _currentImage.Height) return; float zoom = e.Delta > 0 ? 1.1f : 0.9f; float newScale = Math.Clamp(_scale * zoom, 0.1f, 10f); _offset.X = e.Location.X - imgPos.X * newScale; _offset.Y = e.Location.Y - imgPos.Y * newScale; _scale = newScale; ClampOffset(); SafeInvalidate(); } } #endregion #region 辅助方法 private PointF ConvertToImageCoords(Point mousePos) { lock (_imageLock) { if (_currentImage == null) return PointF.Empty; return new PointF( (mousePos.X - _offset.X) / _scale, (mousePos.Y - _offset.Y) / _scale); } } private void ClampOffset() { lock (_imageLock) { if (_currentImage == null) return; float imgWidth = _currentImage.Width * _scale; float imgHeight = _currentImage.Height * _scale; _offset.X = Math.Clamp(_offset.X, imgWidth > Width ? Width - imgWidth : 0, imgWidth > Width ? 0 : Width - imgWidth); _offset.Y = Math.Clamp(_offset.Y, imgHeight > Height ? Height - imgHeight : 0, imgHeight > Height ? 0 : Height - imgHeight); } } private void UpdateStatus(Point mousePos) { if (InvokeRequired) { BeginInvoke(new Action(UpdateStatus), mousePos); return; } lock (_imageLock) { if (_currentImage == null) { statusLabel.Text = "无有效图像"; return; } PointF imgPos = ConvertToImageCoords(mousePos); bool inImage = imgPos.X >= 0 && imgPos.X <= _currentImage.Width && imgPos.Y >= 0 && imgPos.Y <= _currentImage.Height; statusLabel.Text = inImage ? $"坐标: ({imgPos.X:F1}, {imgPos.Y:F1}) | 缩放: {_scale * 100:0}%" : $"图像尺寸: {_currentImage.Width}x{_currentImage.Height} | 缩放: {_scale * 100:0}%"; } } private void SafeInvalidate() { if (InvokeRequired) { BeginInvoke(new Action(pictureBox.Invalidate)); } else { pictureBox.Invalidate(); } } #endregion public Bitmap GetCurrentSnapshot() { lock (_imageLock) { // 返回深拷贝防止原始图像被修改 return _currentImage?.Clone() as Bitmap; } } protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } lock (_imageLock) { _currentImage?.Dispose(); _currentImage = null; } base.Dispose(disposing); } } }