目录
需求
实现效果
开发运行环境
设计实现
界面布局
初始化
画笔绘图
清空画布
导出位图数据
小结
需求
我的文章 《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; } }
画笔类主要包括 :
序号 | 属性名 | 类型 | 说明 |
---|---|---|---|
1 | LastPoint | Point | 记录最后一次画笔的坐标点,并结合 DrawLine 方法画出想要的线段 |
2 | Color | Color | 画笔的颜色,默认为黑色 |
3 | Width | int | 画笔的粗线,默认为2,1为最细 |
实现绘图,主要是通过画笔类,在canvasPanel 的鼠标按下、鼠标移动、和鼠标抬起事件定义相关操作。
序号 | 事件名 | 说明 |
---|---|---|
1 | canvasPanel_MouseDown | 记住鼠标是否按下,将 bool isMouseDown 置为true,另一个关键功能是将按下的点(Point),赋值到画笔的 LastPoint 属性,以备后续绘制线条使用 |
2 | CanvasPanel_MouseMove | 判断 isMouseDown 标志,如果为 true 则引入画布图像,从最后一次的Point结合当前鼠标的Point 进行 DrawLine 操作,并形成新的位图数据 |
3 | CanvasPanel_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 选项,可以导出三种类型的形式数据,如下表:
序号 | 事件名 | 说明 |
---|---|---|
1 | radioButton1 | 直接导出成文件(jpeg类型) |
2 | radioButton2 | 导出二进制数据 (byte[]) |
3 | radioButton3 | 导出 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 通过画布画笔实现绘图的一些介绍,感谢您的阅读,希望本文能够对您有所帮助。