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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80455.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80455.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80455.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ map multimap 容器:賦值、排序、大小與刪除操作

概述 map和multimap是C STL中的關聯容器&#xff0c;它們存儲的是鍵值對(key-value pairs)&#xff0c;并且會根據鍵(key)自動排序。兩者的主要區別在于&#xff1a; map不允許重復的鍵multimap允許重復的鍵 本文將詳細解析示例代碼中涉及的map操作&#xff0c;包括賦值、排…

AI Agent開發第70課-徹底消除RAG知識庫幻覺(4)-解決知識庫問答時語料“總重復”問題

開篇 “解決知識庫幻覺”系列還在繼續,這是因為:如果只是個人玩玩,像自媒體那些說的什么2小時搭一個知識庫+deepseek不要太香一類的RAG或者是基于知識庫的應用肯定是沒法用在企業級落地上的。 我們真的經歷過或者正在經歷的人都是知道的,怎么可能2小時就搭建完成一個知識…

【DAY22】 復習日

內容來自浙大疏錦行python打卡訓練營 浙大疏錦行 仔細回顧一下之前21天的內容 作業&#xff1a; 自行學習參考如何使用kaggle平臺&#xff0c;寫下使用注意點&#xff0c;并對下述比賽提交代碼 kaggle泰坦里克號人員生還預測

【Docker】Docker Compose方式搭建分布式協調服務(Zookeeper)集群

開發分布式應用時,往往需要高度可靠的分布式協調,Apache ZooKeeper 致力于開發和維護開源服務器&#xff0c;以實現高度可靠的分布式協調。具體內容見zookeeper官網。現代應用往往使用云原生技術進行搭建,如何用Docker搭建Zookeeper集群,這里介紹使用Docker Compose方式搭建分布…

若依框架Consul微服務版本

1、最近使用若依前后端分離框架改造為Consul微服務版本 在這里分享出來供大家參考 # Consul微服務配置參數已經放置/bin/Consul微服務配置目錄 倉庫地址&#xff1a; gitee&#xff1a;https://gitee.com/zlxls/Ruoyi-Consul-Cloud.git gitcode&#xff1a;https://gitcode.c…

BOM知識點

BOM&#xff08;Browser Object Model&#xff09;即瀏覽器對象模型&#xff0c;是用于訪問和操作瀏覽器窗口的編程接口。以下是一些BOM的知識點總結&#xff1a; 核心對象 ? window&#xff1a;BOM的核心對象&#xff0c;代表瀏覽器窗口。它也是全局對象&#xff0c;所有全…

什么是遷移學習(Transfer Learning)?

什么是遷移學習&#xff08;Transfer Learning&#xff09;&#xff1f; 一句話概括 遷移學習研究如何把一個源領域&#xff08;source domain&#xff09;/源任務&#xff08;source task&#xff09;中獲得的知識遷移到目標領域&#xff08;target domain&#xff09;/目標任…

[創業之路-362]:企業戰略管理案例分析-3-戰略制定-華為使命、愿景、價值觀的演變過程

一、華為使命、愿景、價值觀的演變過程 1、創業初期&#xff08;1987 - 1994 年&#xff09;&#xff1a;生存導向&#xff0c;文化萌芽 使命愿景雛形&#xff1a;1994年華為提出“10年之后&#xff0c;世界通信行業三分天下&#xff0c;華為將占一份”的宏偉夢想&#xff0c…

Python黑魔法與底層原理揭秘:突破語言邊界的深度探索

Python黑魔法與底層原理揭秘&#xff1a;突破語言邊界的深度探索 開篇&#xff1a;超越表面的Python Python常被稱為"膠水語言"&#xff0c;但其真正的威力在于對底層的高度可控性。本文將揭示那些鮮為人知的Python黑魔法&#xff0c;帶你深入CPython實現層面&…

Es的text和keyword類型以及如何修改類型

昨天同事觸發定時任務發現es相關服務報了一個序列化問題&#xff0c; 今天早上捕獲異常將異常堆棧全部打出來看&#xff0c;才發現是聚合的字段不是keyword類型的問題。 到kibbna命令行執行也是一樣的錯誤 使用 /_mapping查看索引的字段類型&#xff0c;才發現userUniqueid是te…

大語言模型 07 - 從0開始訓練GPT 0.25B參數量 - MiniMind 實機訓練 預訓練 監督微調

寫在前面 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是目前最廣泛應用的大語言模型架構之一&#xff0c;其強大的自然語言理解與生成能力背后&#xff0c;是一個龐大而精細的訓練流程。本文將從宏觀到微觀&#xff0c;系統講解GPT的訓練過程&#xff0c;…

【Android】從Choreographer到UI渲染(二)

【Android】從Choreographer到UI渲染&#xff08;二&#xff09; Google 在 2012 年推出的 Project Butter&#xff08;黃油計劃&#xff09;是 Android 系統發展史上的重要里程碑&#xff0c;旨在解決長期存在的 UI 卡頓、響應延遲等問題&#xff0c;提升用戶體驗。 在 Androi…

mvc-ioc實現

IOC 1&#xff09;耦合/依賴 依賴&#xff0c;是誰離不開誰 就比如上訴的Controller層必須依賴于Service層&#xff0c;Service層依賴于Dao 在軟件系統中&#xff0c;層與層之間存在依賴。我們稱之為耦合 我們系統架構或者設計的一個原則是&#xff…

MATLAB安裝常見問題解決方案

目前新版本的matlab安裝往往需要十幾G的本地安裝容量&#xff0c;例如matlab2022b、matlab2023b, 首先就是要保證本地硬盤空間足夠大&#xff0c;如果沒有足夠的本地內存空間&#xff0c;那么可以嘗試釋放本地硬盤空間&#xff0c;或者安裝所需內存空間較小的舊版本的matlab&am…

程序代碼篇---python獲取http界面上按鈕或者數據輸入

文章目錄 前言 前言 本文簡單接受了python獲取http界面上按鈕或者數據輸入

深入理解 Cortex-M3 特殊寄存器

在上一篇文章中分享了 Cortex-M3 內核寄存器組的相關知識&#xff0c;實際上除了內核寄存器組外&#xff0c;CM3 處理器中還存在多個特殊寄存器&#xff0c;它們分別為 程序狀態寄存器&#xff0c;中斷/異常屏蔽寄存器 和 控制寄存器。 需要注意的是&#xff0c;特殊寄存器未經…

標準庫、HAl庫和LL庫(PC13初始化)

標準庫 (Standard Peripheral Library) c #include "stm32f10x.h"void GPIO_Init_PC13(void) {GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitStruct.GPIO_Pin GPIO_Pin_13;GPIO_InitStruct.GPIO_Mode GPIO_…

基于開源鏈動2+1模式AI智能名片S2B2C商城小程序的低集中度市場運營策略研究

摘要&#xff1a;本文聚焦于行業市場集中度問題&#xff0c;探討在低集中度市場中&#xff0c;如何利用開源鏈動21模式AI智能名片S2B2C商城小程序開展有效運營。分析了高集中度市場的競爭劣勢&#xff0c;闡述了開源鏈動21模式、AI智能名片以及S2B2C商城小程序的功能特點及其在…

一文讀懂-嵌入式Ubuntu平臺

現在直接在一些嵌入式Soc上移植ubuntu來用到產品上&#xff0c;剛開始感覺還挺臃腫的&#xff0c;后來細聊了下感覺還是有一定的優勢。 ubuntu相信大家在熟悉不過了&#xff0c;幾乎無處不在&#xff0c;小到咖啡機&#xff0c;大到火星車&#xff0c;為什么ubuntu如此廣泛&am…

箭頭函數及其與普通函數區別的詳細解釋

一、箭頭函數的基本特性 語法簡潔性 箭頭函數使用 > 符號定義&#xff0c;省略 function 關鍵字&#xff0c;適合快速定義匿名函數或簡單表達式。 // 普通函數 function sum(a, b) { return a b; } // 箭頭函數 const sum (a, b) > a b;若函數體為單行表達式&#x…