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.mzph.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…