当前位置: 首页 > news >正文

C# WinForm实现画笔签名及解决MemoryBmp格式问题

目录

需求

实现效果

开发运行环境

设计实现

界面布局

初始化

画笔绘图

清空画布

导出位图数据

小结


需求

我的文章 《C# 结合JavaScript实现手写板签名并上传到服务器》主要介绍了 web 版的需求实现,本文应项目需求介绍如何通过 C# WinForm 通过画布画笔实现手写签名,并在开发过程中解决遇到的一些格式转换的问题,提供一些思路。

实现效果

签名功能的显示界面如下图:

该效果主要实现如下功能:

1、提供画布,设计画笔类,实现画笔签名

2、点击重签按钮清空画布

3、点击确认按钮保存画布位图到指定的格式(提供三种保存类型,文件,二进制数据和BASE64编码)

开发运行环境

操作系统: Windows Server 2019 DataCenter

手写触屏设备:Microsoft Surface Pro 9

.net版本: .netFramework4.0 或以上

开发工具:VS2019  C#

设计实现

界面布局

主要在WinForm上放置如下控件,Name 为 canvasPanel 的 System.Windows.Forms.Panel控件,一些Label控件、radioButton控件和两个功能按钮Button控件,如下图:

初始化

Form1 初始化如下变量: 

      bool isMouseDown = false;      // 判断鼠标或手指是否按下,按下为 true
      Graphics canvas = null;    // 定义绘图画布
      Image bmpData = null;     // 定义 Image 图像,将来导出时使用

实例化变量的过程中 new Bitmap ,则产生的默认格式为 System.Drawing.Imaging.ImageFormat.MemoryBmp 格式,这会产生一个问题,保存的位图是全黑色。因此一个解决的思路是先临时创建一个白色背景的JPEG图片,图片的大小取决于panel控件的宽度和高度,然后再将画布的图像 bmpData 变量,实例化创建引用这个临时图片的路径。

示例代码如下:

public partial class Form1 : Form{bool isMouseDown = false;Graphics canvas = null;Image bmpData = null;public Form1(){InitializeComponent();canvas = canvasPanel.CreateGraphics();string tmpJpg = Application.StartupPath + "\\tmpimg\\" + System.Guid.NewGuid().ToString() + ".jpg";using (Bitmap bitmap = new Bitmap(canvasPanel.Width, canvasPanel.Height)){using (Graphics graphics = Graphics.FromImage(bitmap)){graphics.Clear(Color.White);}bitmap.Save(tmpJpg, ImageFormat.Jpeg);}bmpData = new Bitmap(tmpJpg);}}

画笔绘图

Graphics canvas 为canvasPanel控件创建的画布,首先定义实现一个画笔类,代码如下:

        public static class signPen{public static Point LastPoint { get; set; }public static Color Color { get; set; }public static int Width { get; set; }static signPen(){Color = Color.Black;Width = 2;}}

画笔类主要包括 :

序号属性名类型说明
1LastPointPoint记录最后一次画笔的坐标点,并结合 DrawLine 方法画出想要的线段
2ColorColor画笔的颜色,默认为黑色
3Widthint画笔的粗线,默认为2,1为最细

实现绘图,主要是通过画笔类,在canvasPanel 的鼠标按下、鼠标移动、和鼠标抬起事件定义相关操作。

序号事件名说明
1canvasPanel_MouseDown记住鼠标是否按下,将 bool isMouseDown 置为true,另一个关键功能是将按下的点(Point),赋值到画笔的 LastPoint 属性,以备后续绘制线条使用
2CanvasPanel_MouseMove判断 isMouseDown 标志,如果为 true 则引入画布图像,从最后一次的Point结合当前鼠标的Point 进行 DrawLine 操作,并形成新的位图数据
3CanvasPanel_MouseUp将 bool isMouseDown 置为 false,不再进行绘制

示例代码如下:

        private void canvasPanel_MouseDown(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){isMouseDown = true;signPen.LastPoint = new Point(e.X, e.Y);}}private void CanvasPanel_MouseMove(object sender, MouseEventArgs e){if (isMouseDown == true){Graphics gf = Graphics.FromImage(bmpData);//设置高质量插值法gf.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;//设置高质量,低速度呈现平滑程度 gf.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;Pen pen = new Pen(signPen.Color, signPen.Width);Point curPoint = new Point(e.X, e.Y);gf.DrawLine(pen, signPen.LastPoint, curPoint);signPen.LastPoint = curPoint;canvas.DrawImage(bmpData, 0,0);}}private void CanvasPanel_MouseUp(object sender, MouseEventArgs e){isMouseDown = false;}

清空画布

可通过点击“重签” 按钮,清空画布,实现如初始化功能,代码如下:

            string tmpJpg = Application.StartupPath + "\\tmpimg\\" + System.Guid.NewGuid().ToString() + ".jpg";using (Bitmap bitmap = new Bitmap(canvasPanel.Width, canvasPanel.Height)){using (Graphics graphics = Graphics.FromImage(bitmap)){graphics.Clear(Color.White);}bitmap.Save(tmpJpg, ImageFormat.Jpeg);}bmpData = new Bitmap(tmpJpg);canvas.DrawImage(bmpData, 0,0);

导出位图数据

绘制完成,我们就需要将 bmpData  位图变量数据导出我们想要的格式,为了便于演示,我们设置了一组 radioButton 选项,可以导出三种类型的形式数据,如下表:

序号事件名说明
1radioButton1直接导出成文件(jpeg类型)
2radioButton2导出二进制数据 (byte[])
3radioButton3导出 base64 数据 (string类型)

假设“确定”按钮 Name 为 “Button13”,并假设输出到D盘根目录下,示例代码如下:

        private void Button13_Click(object sender, EventArgs e){string jpgFilename = "d:\\" + System.Guid.NewGuid().ToString() + ".jpg";bmpData.Save(jpgFilename,ImageFormat.Jpeg);if (File.Exists(jpgFilename) == false){MessageBox.Show(string.Format("保存文件至{0}失败。", jpgFilename));return;}if (radioButton1.Checked == true){MessageBox.Show(string.Format("已成功保存至{0}。", jpgFilename));}if (radioButton2.Checked == true){byte[] bytes2=fe.GetBinaryData(jpgFilename);MessageBox.Show(string.Format("已成功保存为二进制数据,长度{0}。", bytes2.Length));}if (radioButton3.Checked == true){string base64str = ImgToBase64String(jpgFilename, false);MessageBox.Show(string.Format("已成功保存为BASE64,长度{0}。", base64str.Length));}}public byte[] GetBinaryData(string filename){if(!File.Exists(filename)){return null;}try{FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);byte[] imageData = new Byte[fs.Length];fs.Read( imageData, 0,Convert.ToInt32(fs.Length));fs.Close();return imageData;}catch(Exception){return null;}finally{}}		public string ImgToBase64String(string Imagefilename, bool outFullString = false){try{System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Imagefilename);MemoryStream ms = new MemoryStream();//            bmp.Save(ms,ImageFormat.Jpeg)System.Drawing.Imaging.ImageFormat iformat = System.Drawing.Imaging.ImageFormat.Jpeg;string extension = System.IO.Path.GetExtension(Imagefilename).Replace(".", "").ToLower();if (extension == "bmp"){iformat = System.Drawing.Imaging.ImageFormat.Bmp;}else if (extension == "emf"){iformat = System.Drawing.Imaging.ImageFormat.Emf;}else if (extension == "exif"){iformat = System.Drawing.Imaging.ImageFormat.Exif;}else if (extension == "gif"){iformat = System.Drawing.Imaging.ImageFormat.Gif;}else if (extension == "icon"){iformat = System.Drawing.Imaging.ImageFormat.Icon;}else if (extension == "png"){iformat = System.Drawing.Imaging.ImageFormat.Png;}else if (extension == "tiff"){iformat = System.Drawing.Imaging.ImageFormat.Tiff;}else if (extension == "wmf"){iformat = System.Drawing.Imaging.ImageFormat.Wmf;}bmp.Save(ms, iformat);byte[] arr = new byte[ms.Length];ms.Position = 0;ms.Read(arr, 0, (int)ms.Length);ms.Close();bmp.Dispose();string rv = Convert.ToBase64String(arr);if (outFullString == true){rv = "data:image/" + extension + ";base64," + rv;}return rv;}catch (Exception ex){return null;}}

小结

对于 new Bitmap 创建的位图,我们还可以使用 Png 格式,以防止“黑图”的出现,我们在应用中可以灵活掌握,如下代码:

    Bitmap newimg = new Bitmap(100,100);newimg.Save("d:\\test.jpg", System.Drawing.Imaging.ImageFormat.Png);

保存的数据,显示在画布上可采取如下方法:

1、文件型

            System.Drawing.Image img2 = new Bitmap(你的文件地址);canvas.DrawImage(img2, 0, 0);MessageBox.Show("显示文件到画布成功!");

2、二进制型

                byte[] bytes = 你的二进制数据;MemoryStream ms = new MemoryStream(bytes);System.Drawing.Image img = System.Drawing.Image.FromStream(ms);canvas.DrawImage(img, 0, 0);MessageBox.Show("显示二进制到画布成功!");

3、base64型

string base64 = 你的base64数据;byte[] arr = Convert.FromBase64String(base64);MemoryStream ms2 = new MemoryStream(arr);System.Drawing.Image img2 = System.Drawing.Image.FromStream(ms2);canvas.DrawImage(img2, 0, 0);MessageBox.Show("显示base64到画布成功!");

以上就是C# WinForm 通过画布画笔实现绘图的一些介绍,感谢您的阅读,希望本文能够对您有所帮助。

相关文章:

C# WinForm实现画笔签名及解决MemoryBmp格式问题

目录 需求 实现效果 开发运行环境 设计实现 界面布局 初始化 画笔绘图 清空画布 导出位图数据 小结 需求 我的文章 《C# 结合JavaScript实现手写板签名并上传到服务器》主要介绍了 web 版的需求实现,本文应项目需求介绍如何通过 C# WinForm 通过画布画笔…...

GC1272替代APX9172/茂达中可应用于电脑散热风扇应用分析

在电脑散热风扇应用中,选择合适的驱动器件对于风扇的性能和效率至关重要。以下是对GC1272替代APX9172/茂达在此类应用中的分析: 1. 功能比较 GC1272: 主要用于驱动直流风扇,具有高效的电流控制和调速功能。支持PWM调速&#xff0…...

《Linux从小白到高手》综合应用篇:详解Linux系统调优之服务器硬件优化

List item 本篇介绍Linux服务器硬件调优。硬件调优主要包括CPU、内存、磁盘、网络等关键硬件组。 1. CPU优化 选择适合的CPU: –根据应用需求选择多核、高频的CPU,以满足高并发和计算密集型任务的需求。CPU缓存优化: –确保CPU缓存&#x…...

PHP政务招商系统——高效连接共筑发展蓝图

政务招商系统——高效连接,共筑发展蓝图 🏛️ 一、政务招商系统:开启智慧招商新篇章 在当今经济全球化的背景下,政务招商成为了推动地方经济发展的重要引擎。而政务招商系统的出现,更是为这一进程注入了新的活力。它…...

Linux 命令行

这学期是我第一次正式学习 linux ,是在 VMware 里创建了 openEuler 的虚拟机练习 linux 的常用命令。 目前主要在学习 linux 的常用命令,因此这篇博客主要介绍一些常用的命令。 本文将持续更新… 阅读建议 Linux 是一个倒置的树结构(文件系…...

每日一题:单例模式

每日一题:单例模式 ❝ 单例模式是确保一个类只有一个实例,并提供一个全局访问点 1.饿汉式(静态常量) 特点:在类加载时就创建了实例。优点:简单易懂,线程安全。缺点:无论是否使用&…...

前端_001_html扫盲

文章目录 概念标签及属性常用全局属性head里常用标签body里常用标签表情符号 url编码 概念 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body></bod…...

49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?

上一篇文章我们学习了第一种结构型模式&#xff1a;代理模式。它在不改变原始类&#xff08;或者叫被代理类&#xff09;代码的情况下&#xff0c;通过引入代理类来给原始类附加功能。代理模式在平时的开发经常被用到&#xff0c;常用在业务系统中开发一些非功能性需求&#xf…...

使用js和canvas实现简单的网页贪吃蛇小游戏

玩法介绍 点击开始游戏后&#xff0c;使用键盘上的↑↓←→控制移动&#xff0c;吃到食物增加长度&#xff0c;碰到墙壁或碰到自身就游戏结束 代码实现 代码比较简单&#xff0c;直接阅读注释即可&#xff0c;复制即用 <!DOCTYPE html> <html lang"en"…...

Kafka SASL/PLAIN认证模式

Kafka 认证模式命令使用示例 创建Topic 指定用户创建 [rootkafka01 kraft]# /usr/local/kafka3.5-sasl-data/bin/kafka-topics.sh --bootstrap-server x.x.x.11:9092 --create --topic fkaaa35 --replication-factor 3 --partitions 3 --command-config /usr/local/kafka3.…...

苹果AI科学家研究证明基于LLM的模型存在缺陷 因为它们无法推理

苹果公司人工智能科学家的一篇新论文发现&#xff0c;基于大型语言模型的引擎&#xff08;如 Meta 和 OpenAI 的引擎&#xff09;仍然缺乏基本的推理能力。该小组提出了一个新的基准–GSM-Symbolic&#xff0c;以帮助其他人衡量各种大型语言模型&#xff08;LLM&#xff09;的推…...

鸿蒙NEXT开发-页面路由(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…...

asp.net Core MVC 内容协商

内容协商 内容协商是Asp.Net Core 控制器的一项功能&#xff0c;而Asp.Net MVC5 控制器并不支持它。 引入内容协商是为了满足 Web API 框架的需要。 在 Asp.net Core 中&#xff0c;内容协商 被内置到引擎中&#xff0c;供开发人员使用。 顾名思义&#xff0c;内容协商指的是…...

智能EDA小白从0开始 —— DAY10 Yosys

Yosys 概述 工作原理 Yosys的工作原理深入来讲&#xff0c;是一个复杂但有序的硬件设计自动化流程&#xff0c;其核心在于将高级硬件描述语言&#xff08;HDL&#xff09;如Verilog或VHDL编写的代码&#xff0c;通过一系列精细的步骤转换为门级网表。这一流程首先涉及对HDL代…...

《OpenCV计算机视觉》—— 人脸检测

文章目录 一、人脸检测流程介绍二、用于人脸检测的关键方法1.加载分类器&#xff08;cv2.CascadeClassifier()&#xff09;2.检测图像中的人脸&#xff08;cv2.CascadeClassifier.detectMultiscale()&#xff09; 三、代码实现 一、人脸检测流程介绍 下面是一张含有多个人脸的…...

【unity框架开发12】从零手搓unity存档存储数据持久化系统,实现对存档的创建,获取,保存,加载,删除,缓存,加密,支持多存档

文章目录 前言一、Unity对Json数据的操作方法一、JsonUtility方法二、Newtonsoft 二、持久化的数据路径三、数据加密/解密加密方法解密方法 四、条件编译指令限制仅在编辑器模式下进行加密/解密四、数据持久化管理器1、存档工具类2、一个存档数据3、存档系统数据类4、数据存档存…...

YOLOv11进行图像与视频的目标检测

一、AI应用系统实战项目 项目名称项目名称1.人脸识别与管理系统2.车牌识别与管理系统...

SpinalHDL之错误集(一)

本文作为SpinalHDL学习笔记第七十六篇&#xff0c;作为错误集使用&#xff0c;类似高中生的错题集&#xff0c;记录使用SpinalHDL过程中遇到的问题&#xff0c;小到语法错误、版本兼容问题&#xff0c;大到SpinalHDL库函数错误等等&#xff0c;持续更新。 SpinalHDL学习笔记总…...

【arcgis】ArcGIS中如何避免标注压盖要素

ArcGIS中如何避免标注压盖要素 在制图工作中&#xff0c;标注&#xff08;Label&#xff09;是传达地理信息的重要方式。然而&#xff0c;在复杂的地图上&#xff0c;标注容易出现压盖要素的情况&#xff0c;影响地图的美观性和信息的准确传达。ArcGIS提供了Maplex标注引擎&am…...

数通--3

一、动态路由 内部 路由器之间要互联互通&#xff0c;必须遵循相同的协议 企业内部用 IGP&#xff0c;企业之间用BGP RIP&#xff08;已淘汰&#xff0c;不考&#xff09; 距离就是长短&#xff0c;矢量就是方向&#xff0c;即路由的出接口 一台路由器 A 配好RIP&#xff0c;…...

League-Toolkit:智能辅助驱动的英雄联盟效率提升解决方案

League-Toolkit&#xff1a;智能辅助驱动的英雄联盟效率提升解决方案 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League-Tool…...

从‘画图’到‘造芯’:模拟版图工程师必须懂的CMOS工艺那些事儿

从‘画图’到‘造芯’&#xff1a;模拟版图工程师必须懂的CMOS工艺那些事儿 当你第一次打开PDK文档&#xff0c;面对密密麻麻的设计规则表格时&#xff0c;是否感觉像在解读天书&#xff1f;作为模拟版图工程师&#xff0c;我们每天都在与纳米级的几何图形打交道&#xff0c;但…...

Qwen3.5-4B-Claude-Opus实战案例:用该模型辅助撰写RFC文档与技术决策说明

Qwen3.5-4B-Claude-Opus实战案例&#xff1a;用该模型辅助撰写RFC文档与技术决策说明 1. 模型特性与RFC文档撰写需求 Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF作为一款专注于推理分析的AI模型&#xff0c;其结构化思维和分步骤回答能力特别适合技术文档撰写场景…...

高防服务器怎么选?360CDN 高防性价比分析

作为运维中小站点3年的老站长&#xff0c;前阵子被DDoS攻击搞得焦头烂额&#xff0c;网站频繁卡顿、宕机&#xff0c;损失不少流量。试过普通服务器加防护插件&#xff0c;基本形同虚设&#xff0c;后来陆续测试了360CDN高防以及其他几款主流高防产品&#xff0c;全程实测不吹不…...

终极Modbus测试工具:OpenModScan让工业通讯调试变得前所未有的简单

终极Modbus测试工具&#xff1a;OpenModScan让工业通讯调试变得前所未有的简单 【免费下载链接】OpenModScan Open ModScan is a Free Modbus Master (Client) Utility 项目地址: https://gitcode.com/gh_mirrors/op/OpenModScan 在工业自动化领域&#xff0c;Modbus协议…...

LeetCode 53. 最大子数组和 超详细题解(贪心+分治+动规)

LeetCode 53. 最大子数组和 超详细题解&#xff08;贪心分治动规&#xff09; &#x1f3f7;️ 标签&#xff1a;动态规划、贪心算法、分治法、数组、经典面试题 &#x1f4ca; 难度&#xff1a;简单 | &#x1f4dd; 题目编号&#xff1a;53 | &#x1f5c2;️ 题型&#xff1…...

华为防火墙NAT映射选择指南:一对一映射 vs 端口映射

华为防火墙NAT映射技术深度解析&#xff1a;一对一映射与端口映射的实战选择 在当今企业网络架构中&#xff0c;如何安全高效地将内部服务暴露给外部访问是一个永恒的技术挑战。华为防火墙提供的NAT映射功能&#xff0c;特别是一对一映射和端口映射两种核心方案&#xff0c;为不…...

7个web.py代码重构技巧:如何快速优化Python Web应用代码结构

7个web.py代码重构技巧&#xff1a;如何快速优化Python Web应用代码结构 【免费下载链接】webpy web.py is a web framework for python that is as simple as it is powerful. 项目地址: https://gitcode.com/gh_mirrors/we/webpy web.py 是一个简单而强大的 Python W…...

3步构建工业级语音数据集:从混乱录音到AI训练素材的蜕变之路

3步构建工业级语音数据集&#xff1a;从混乱录音到AI训练素材的蜕变之路 你是否还在为语音识别模型效果不佳而烦恼&#xff1f;是否采集了大量语音却不知如何转化为训练数据&#xff1f;本文将带你通过FunASR框架提供的标准化工具链&#xff0c;3步完成工业级语音数据集的构建…...

M3U8 开发调试神器!m3u8live.cn轻量在线播放器高效解决流媒体开发痛点

在音视频开发、直播推流、点播平台搭建的日常工作中&#xff0c;M3U8 链接有效性验证、HLS 流播放调试是高频刚需。传统方案要么需要安装 VLC 等本地播放器进行繁琐的网络串流配置&#xff0c;要么第三方工具广告泛滥、兼容性差&#xff0c;甚至需要编写测试代码才能完成简单的…...