using System.Drawing.Drawing2D; using WorkCameraExport.Services; namespace WorkCameraExport.Forms { /// /// 图片查看器窗体 - 支持缩放、拖拽、轮播 /// Requirements: 3.3, 3.4, 3.5, 3.6 /// public partial class ImageViewerForm : Form { private readonly List _imageUrls; private readonly Dictionary _imageCache; private readonly ApiService? _apiService; private int _currentIndex; private float _zoomFactor = 1.0f; private PointF _imageOffset; private Point _lastMousePosition; private bool _isDragging; private Image? _currentImage; // 缩放限制 private const float MinZoom = 0.1f; private const float MaxZoom = 10.0f; private const float ZoomStep = 0.1f; /// /// 当前图片索引 /// public int CurrentIndex { get => _currentIndex; private set { if (_imageUrls.Count == 0) return; // 循环索引 _currentIndex = WrapIndex(value, _imageUrls.Count); UpdateDisplay(); } } /// /// 图片总数 /// public int ImageCount => _imageUrls.Count; /// /// 构造函数 /// /// 图片 URL 列表 /// 起始索引 /// API 服务(用于下载图片) public ImageViewerForm(List imageUrls, int startIndex = 0, ApiService? apiService = null) { InitializeComponent(); _imageUrls = imageUrls ?? new List(); _imageCache = new Dictionary(); _apiService = apiService; _currentIndex = WrapIndex(startIndex, Math.Max(1, _imageUrls.Count)); _imageOffset = PointF.Empty; SetupEventHandlers(); if (_imageUrls.Count > 0) { _ = LoadCurrentImageAsync(); } } /// /// 设置事件处理器 /// private void SetupEventHandlers() { // 图片面板事件 panelImage.Paint += PanelImage_Paint; panelImage.MouseWheel += PanelImage_MouseWheel; panelImage.MouseDown += PanelImage_MouseDown; panelImage.MouseMove += PanelImage_MouseMove; panelImage.MouseUp += PanelImage_MouseUp; panelImage.Resize += PanelImage_Resize; // 键盘事件 this.KeyPreview = true; this.KeyDown += ImageViewerForm_KeyDown; } #region 图片导航 /// /// 显示下一张图片 /// public void NextImage() { if (_imageUrls.Count <= 1) return; CurrentIndex = _currentIndex + 1; } /// /// 显示上一张图片 /// public void PreviousImage() { if (_imageUrls.Count <= 1) return; CurrentIndex = _currentIndex - 1; } /// /// 显示指定索引的图片 /// public void ShowImage(int index) { CurrentIndex = index; } /// /// 循环索引计算 /// 确保索引始终在 [0, count-1] 范围内 /// public static int WrapIndex(int index, int count) { if (count <= 0) return 0; // 处理负数索引 index = index % count; if (index < 0) index += count; return index; } #endregion #region 图片加载 /// /// 加载当前图片 /// private async Task LoadCurrentImageAsync() { if (_imageUrls.Count == 0) return; var url = _imageUrls[_currentIndex]; lblStatus.Text = $"正在加载图片 {_currentIndex + 1}/{_imageUrls.Count}..."; lblStatus.ForeColor = Color.Blue; try { // 检查缓存 if (_imageCache.TryGetValue(url, out var cachedImage) && cachedImage != null) { _currentImage = cachedImage; ResetView(); UpdateStatusLabel(); panelImage.Invalidate(); return; } // 下载图片 byte[]? imageData = null; if (_apiService != null) { var (success, data, _) = await _apiService.DownloadImageAsync(url, CancellationToken.None); if (success) imageData = data; } else { // 直接使用 HttpClient 下载 using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; imageData = await httpClient.GetByteArrayAsync(url); } if (imageData != null && imageData.Length > 0) { using var ms = new MemoryStream(imageData); var image = Image.FromStream(ms); _imageCache[url] = image; _currentImage = image; ResetView(); UpdateStatusLabel(); } else { _currentImage = null; lblStatus.Text = "图片加载失败"; lblStatus.ForeColor = Color.Red; } } catch (Exception ex) { _currentImage = null; lblStatus.Text = $"加载失败: {ex.Message}"; lblStatus.ForeColor = Color.Red; } panelImage.Invalidate(); } /// /// 更新显示 /// private void UpdateDisplay() { _ = LoadCurrentImageAsync(); UpdateNavigationButtons(); } /// /// 更新状态标签 /// private void UpdateStatusLabel() { if (_currentImage != null) { lblStatus.Text = $"图片 {_currentIndex + 1}/{_imageUrls.Count} | " + $"尺寸: {_currentImage.Width}x{_currentImage.Height} | " + $"缩放: {_zoomFactor:P0}"; lblStatus.ForeColor = Color.FromArgb(51, 51, 51); } } /// /// 更新导航按钮状态 /// private void UpdateNavigationButtons() { btnPrevious.Enabled = _imageUrls.Count > 1; btnNext.Enabled = _imageUrls.Count > 1; lblImageIndex.Text = _imageUrls.Count > 0 ? $"{_currentIndex + 1} / {_imageUrls.Count}" : "0 / 0"; } #endregion #region 缩放功能 /// /// 放大图片 /// public void ZoomIn() { SetZoom(_zoomFactor + ZoomStep); } /// /// 缩小图片 /// public void ZoomOut() { SetZoom(_zoomFactor - ZoomStep); } /// /// 重置缩放 /// public void ResetZoom() { _zoomFactor = 1.0f; _imageOffset = PointF.Empty; UpdateStatusLabel(); panelImage.Invalidate(); } /// /// 适应窗口大小 /// public void FitToWindow() { if (_currentImage == null) return; var panelWidth = panelImage.ClientSize.Width; var panelHeight = panelImage.ClientSize.Height; var imageWidth = _currentImage.Width; var imageHeight = _currentImage.Height; var scaleX = (float)panelWidth / imageWidth; var scaleY = (float)panelHeight / imageHeight; _zoomFactor = Math.Min(scaleX, scaleY); _zoomFactor = Math.Clamp(_zoomFactor, MinZoom, MaxZoom); _imageOffset = PointF.Empty; UpdateStatusLabel(); panelImage.Invalidate(); } /// /// 设置缩放比例 /// private void SetZoom(float zoom) { _zoomFactor = Math.Clamp(zoom, MinZoom, MaxZoom); UpdateStatusLabel(); panelImage.Invalidate(); } /// /// 重置视图 /// private void ResetView() { _zoomFactor = 1.0f; _imageOffset = PointF.Empty; FitToWindow(); } #endregion #region 绘制 /// /// 绘制图片 /// private void PanelImage_Paint(object? sender, PaintEventArgs e) { var g = e.Graphics; g.Clear(Color.FromArgb(30, 30, 30)); g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.HighQuality; if (_currentImage == null) { // 显示无图片提示 using var font = new Font("Microsoft YaHei UI", 14f); using var brush = new SolidBrush(Color.Gray); var text = _imageUrls.Count == 0 ? "没有图片" : "图片加载中..."; var size = g.MeasureString(text, font); var x = (panelImage.Width - size.Width) / 2; var y = (panelImage.Height - size.Height) / 2; g.DrawString(text, font, brush, x, y); return; } // 计算图片绘制位置和大小 var scaledWidth = _currentImage.Width * _zoomFactor; var scaledHeight = _currentImage.Height * _zoomFactor; // 居中显示 var centerX = (panelImage.Width - scaledWidth) / 2 + _imageOffset.X; var centerY = (panelImage.Height - scaledHeight) / 2 + _imageOffset.Y; var destRect = new RectangleF(centerX, centerY, scaledWidth, scaledHeight); var srcRect = new RectangleF(0, 0, _currentImage.Width, _currentImage.Height); g.DrawImage(_currentImage, destRect, srcRect, GraphicsUnit.Pixel); } #endregion #region 鼠标事件 /// /// 鼠标滚轮缩放 /// private void PanelImage_MouseWheel(object? sender, MouseEventArgs e) { if (_currentImage == null) return; var delta = e.Delta > 0 ? ZoomStep : -ZoomStep; SetZoom(_zoomFactor + delta); } /// /// 鼠标按下 - 开始拖拽 /// private void PanelImage_MouseDown(object? sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left && _currentImage != null) { _isDragging = true; _lastMousePosition = e.Location; panelImage.Cursor = Cursors.Hand; } } /// /// 鼠标移动 - 拖拽图片 /// private void PanelImage_MouseMove(object? sender, MouseEventArgs e) { if (_isDragging && _currentImage != null) { var deltaX = e.X - _lastMousePosition.X; var deltaY = e.Y - _lastMousePosition.Y; _imageOffset = new PointF( _imageOffset.X + deltaX, _imageOffset.Y + deltaY); _lastMousePosition = e.Location; panelImage.Invalidate(); } } /// /// 鼠标释放 - 结束拖拽 /// private void PanelImage_MouseUp(object? sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { _isDragging = false; panelImage.Cursor = Cursors.Default; } } /// /// 面板大小改变 /// private void PanelImage_Resize(object? sender, EventArgs e) { panelImage.Invalidate(); } #endregion #region 键盘事件 /// /// 键盘按键处理 /// private void ImageViewerForm_KeyDown(object? sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Left: case Keys.A: PreviousImage(); e.Handled = true; break; case Keys.Right: case Keys.D: NextImage(); e.Handled = true; break; case Keys.Add: case Keys.Oemplus: ZoomIn(); e.Handled = true; break; case Keys.Subtract: case Keys.OemMinus: ZoomOut(); e.Handled = true; break; case Keys.D0: case Keys.NumPad0: ResetZoom(); e.Handled = true; break; case Keys.F: FitToWindow(); e.Handled = true; break; case Keys.Escape: this.Close(); e.Handled = true; break; } } #endregion #region 按钮事件 /// /// 上一张按钮点击 /// private void btnPrevious_Click(object sender, EventArgs e) { PreviousImage(); } /// /// 下一张按钮点击 /// private void btnNext_Click(object sender, EventArgs e) { NextImage(); } /// /// 放大按钮点击 /// private void btnZoomIn_Click(object sender, EventArgs e) { ZoomIn(); } /// /// 缩小按钮点击 /// private void btnZoomOut_Click(object sender, EventArgs e) { ZoomOut(); } /// /// 重置按钮点击 /// private void btnReset_Click(object sender, EventArgs e) { ResetZoom(); } /// /// 适应窗口按钮点击 /// private void btnFit_Click(object sender, EventArgs e) { FitToWindow(); } /// /// 关闭按钮点击 /// private void btnClose_Click(object sender, EventArgs e) { this.Close(); } #endregion #region 清理 /// /// 窗体关闭时清理资源 /// protected override void OnFormClosed(FormClosedEventArgs e) { base.OnFormClosed(e); // 清理图片缓存 foreach (var image in _imageCache.Values) { image?.Dispose(); } _imageCache.Clear(); _currentImage = null; } #endregion } }