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 通过画布画笔实现绘图的一些介绍,感谢您的阅读,希望本文能够对您有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/446519.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Appium环境搭建、Appium连接真机

文章目录 一、安装Android SDK二、安装Appium-desktop三、安装Appium Inspector 一、安装Android SDK 首先需要安装jdk,这里就不演示安装jdk的过程了 SDK下载地址:Android SDK 下载 1、点击 Android SDK 下载 -> SKD Tools 2、选择对应的版本进行下…

SpringBoot基础(五):集成JUnit5

SpringBoot基础系列文章 SpringBoot基础(一):快速入门 SpringBoot基础(二):配置文件详解 SpringBoot基础(三):Logback日志 SpringBoot基础(四):bean的多种加载方式 SpringBoot基础(五):集成JUnit5 目录 一、JUnit…

前端开发设计模式——组合模式

目录 一、组合模式的定义和特点 1.定义 2.特点: 二、组合模式的实现方式 1.定义抽象组件类 2.创建叶节点类 3.创建组合类: 三、组合模式的应用场景 1.界面布局管理 2.菜单系统构建 3.组件库开发 四、组合模式的优点 1.简化客户端代码 2.增…

GO网络编程(七):海量用户通信系统5:分层架构

P323开始(尚硅谷GO教程)老韩又改目录结构了,没办法,和之前一样,先说下目录结构,再给代码,部分代码在之前讲过,还有知识的话由于本人近期很忙,所以这些就不多赘述了&#…

【C++】12.string类的使用

文章目录 1. 为什么学习string类?1.1 C语言中的字符串1.2 两个面试题(暂不做讲解) 2. 标准库中的string类2.1 string类(了解)2.2 auto和范围for 3. 查看技术文档4. string的访问5. 如何读取每个字符呢?6. auto语法糖(C11)7. 范围f…

浅析主流监控告警系统基本架构和原理

浅析主流监控告警系统基本架构和原理 一,监控系统的作用和目前主流监控系统 1,作用:监控系统一般有以下这几个作用 实时采集监控数据:包括硬件、操作系统、中间件、应用程序等各个维度的数据。实时反馈监控状态:通过…

【目标检测】集装箱缺陷检测数据集1476张5类缺陷VOC+YOLO格式

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):1476 标注数量(xml文件个数):1476 标注数量(txt文件个数):1476 标注…

ubuntu下打开摄像头

ubuntu下打开摄像头 在Ubuntu下,你可以使用cheese,这是一个开源的摄像头应用程序。如果你还没有安装它,可以通过以下命令安装: sudo apt-get updatesudo apt-get install cheese 安装完成后,你可以通过命令行启动它: cheese 或者,你也可以使用ffmpeg来打开摄像头并进…

MATLAB - 机器人机械臂设计轨迹规划器

系列文章目录 前言 本示例介绍了一种设计抓取和轨迹规划器的方法,该规划器可用于垃圾箱拣选系统。 在机器人技术中,垃圾箱拣选包括使用机械手从垃圾箱中取出物品。智能垃圾箱拣选是这一过程的高级版本,具有更强的自主性。使用摄像系统感知部件,规划器生成与场景相适应的无碰…

Telegram——Bot 机器人/小程序入门指南

一、Bot 介绍 在 TG 中,机器人可以用于接收和发送消息、管理群组(在有权限的情况下可以封禁用户、删除消息、置顶消息等)、通过API进行编程操作、使用 Inline 查询功能在不同的聊天室中提供查询服务、创建自定义键盘按钮、发出账单并收款、接入小程序游戏等。 然而,Bot 默…

智汇云舟亮相WAFI世界农业科技创新大会,并参编数字农业产业图谱

10月10日,2024WAFI世界农业科技创新大会农食行业创新与投资峰会在北京金海湖国际会展中心举行。中国农业大学MBA教育中心主任、教授付文阁、平谷区委常委、统战部部长刘堃、华为公共事业军团数字政府首席专家刘丹、荷兰瓦赫宁根大学前校长Aalt Dijkhuizen、牧原食品…

免费送源码:Java+Springboot+MySQL 水环境检测系统的设计与实现 计算机毕业设计原创定制

摘 要 在我国,水源的污染是不可忽视的问题。对于水质监测进行数据的采集工作,目前主要通过人工实现。因此,部分地区的采集工作,实施起来难度很大,比如恶劣环境和偏僻山区等地。所以,目前对于水质监测的研究,主导方向是建立更加高效完善,智能化的水质监测系统。近几年,无线传感器…

RWKV-CHN模型部署教程

一、模型介绍 RWKV 语言模型(用纯 100%RNN 达到 GPT 能力,甚至更强),该项目旨在通过为您自动化所有事情来消除使用大型语言模型的障碍。您需要的是一个只有几兆字节的轻量级可执行程序。此外,该项目还提供了一个接口兼…

计算机网络——p2p

流媒体是指在网络上以流式传输技术实时播放的多媒体内容,如音频、视频等。以下是关于流媒体的详细介绍: 一、工作原理 数据分割与传输: 流媒体技术将多媒体文件分割成较小的数据包。这些数据包按照特定的顺序进行编号,然后通过网络…

[单master节点k8s部署]40.安装harbor

harbor 是私有镜像仓库,用来存储和分发镜像的 。docker 还有一个官方的镜像仓库 docker hub,免费用户只能简单的使用,创建一个私有镜像仓库,存储镜像,付费用户才可以拥有更多权限,默认 docker pull 拉取镜像…

网络学习第二篇

认识网关和路由器 这里大家先了解一下什么三层设备。 三层设备 三层设备是指在网络架构中能够工作在第三层(网络层)的设备,通常包括三层交换机和路由器。这些设备可以根据IP地址进行数据包的转发和路由选择,从而在不同的网络之间…

<<迷雾>> 第11章 全自动加法计算机(5)--顺序取数 示例电路

顺序地从存储器里取数的电路方案. info::操作说明 在开始之前, 地址计数器 AC 需要清零, 以指向地址 0000. 按一下开关 KAR, 将 AC 当前的地址锁存到 AR 地址寄存器. 按住 KRD, 不要松开(注: 系统中使用的是普通开关, 无需按住), 再按一下 KDR, 数据保存到寄存器 DR 中, 最后,…

Unity3D 观察者模式

Unity3D 泛型事件系统 观察者模式 观察者模式是一种行为设计模式,通过订阅机制,可以让对象触发事件时,通知多个其他对象。 在游戏逻辑中,UI 界面通常会监听一些事件,当数据层发生变化时,通过触发事件&am…

多人播客的生成#使用OpenAI Swarm框架

使用Swarm来写多智能体的代码,非常简洁高效。 什么是Swarm? Swarm是由OpenAI开发的一个实验性多代理系统框架,旨在探索多代理系统的高效接口。该框架注重轻量级、可控性高且易于测试,主要用于展示代理之间的交接与例行操作模式。S…

基于SpringBoot的校园兼职管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…