C# WinForm實現(xiàn)畫筆簽名功能
需求
我的文章 《C# 結(jié)合JavaScript實現(xiàn)手寫板簽名并上傳到服務(wù)器》主要介紹了 web 版的需求實現(xiàn),本文應(yīng)項目需求介紹如何通過 C# WinForm 通過畫布畫筆實現(xiàn)手寫簽名,并在開發(fā)過程中解決遇到的一些格式轉(zhuǎn)換的問題,提供一些思路。
實現(xiàn)效果
簽名功能的顯示界面如下圖:
該效果主要實現(xiàn)如下功能:
1、提供畫布,設(shè)計畫筆類,實現(xiàn)畫筆簽名
2、點擊重簽按鈕清空畫布
3、點擊確認(rèn)按鈕保存畫布位圖到指定的格式(提供三種保存類型,文件,二進制數(shù)據(jù)和BASE64編碼)
開發(fā)運行環(huán)境
操作系統(tǒng): Windows Server 2019 DataCenter
手寫觸屏設(shè)備:Microsoft Surface Pro 9
.net版本: .netFramework4.0 或以上
開發(fā)工具:VS2019 C#
設(shè)計實現(xiàn)
界面布局
主要在WinForm上放置如下控件,Name 為 canvasPanel 的 System.Windows.Forms.Panel控件,一些Label控件、radioButton控件和兩個功能按鈕Button控件,如下圖:
初始化
Form1 初始化如下變量:
bool isMouseDown = false; // 判斷鼠標(biāo)或手指是否按下,按下為 true Graphics canvas = null; // 定義繪圖畫布 Image bmpData = null; // 定義 Image 圖像,將來導(dǎo)出時使用
實例化變量的過程中 new Bitmap ,則產(chǎn)生的默認(rèn)格式為 System.Drawing.Imaging.ImageFormat.MemoryBmp 格式,這會產(chǎn)生一個問題,保存的位圖是全黑色。因此一個解決的思路是先臨時創(chuàng)建一個白色背景的JPEG圖片,圖片的大小取決于panel控件的寬度和高度,然后再將畫布的圖像 bmpData 變量,實例化創(chuàng)建引用這個臨時圖片的路徑。
示例代碼如下:
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控件創(chuàng)建的畫布,首先定義實現(xiàn)一個畫筆類,代碼如下:
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 | 記錄最后一次畫筆的坐標(biāo)點,并結(jié)合 DrawLine 方法畫出想要的線段 |
2 | Color | Color | 畫筆的顏色,默認(rèn)為黑色 |
3 | Width | int | 畫筆的粗線,默認(rèn)為2,1為最細 |
實現(xiàn)繪圖,主要是通過畫筆類,在canvasPanel 的鼠標(biāo)按下、鼠標(biāo)移動、和鼠標(biāo)抬起事件定義相關(guān)操作。
序號 | 事件名 | 說明 |
---|---|---|
1 | canvasPanel_MouseDown | 記住鼠標(biāo)是否按下,將 bool isMouseDown 置為true,另一個關(guān)鍵功能是將按下的點(Point),賦值到畫筆的 LastPoint 屬性,以備后續(xù)繪制線條使用 |
2 | CanvasPanel_MouseMove | 判斷 isMouseDown 標(biāo)志,如果為 true 則引入畫布圖像,從最后一次的Point結(jié)合當(dāng)前鼠標(biāo)的Point 進行 DrawLine 操作,并形成新的位圖數(shù)據(jù) |
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); //設(shè)置高質(zhì)量插值法 gf.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; //設(shè)置高質(zhì)量,低速度呈現(xiàn)平滑程度 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; }
清空畫布
可通過點擊“重簽” 按鈕,清空畫布,實現(xiàn)如初始化功能,代碼如下:
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);
導(dǎo)出位圖數(shù)據(jù)
繪制完成,我們就需要將 bmpData 位圖變量數(shù)據(jù)導(dǎo)出我們想要的格式,為了便于演示,我們設(shè)置了一組 radioButton 選項,可以導(dǎo)出三種類型的形式數(shù)據(jù),如下表:
序號 | 事件名 | 說明 |
---|---|---|
1 | radioButton1 | 直接導(dǎo)出成文件(jpeg類型) |
2 | radioButton2 | 導(dǎo)出二進制數(shù)據(jù) (byte[]) |
3 | radioButton3 | 導(dǎo)出 base64 數(shù)據(jù) (string類型) |
假設(shè)“確定”按鈕 Name 為 “Button13”,并假設(shè)輸出到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("已成功保存為二進制數(shù)據(jù),長度{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; } }
小結(jié)
對于 new Bitmap 創(chuàng)建的位圖,我們還可以使用 Png 格式,以防止“黑圖”的出現(xiàn),我們在應(yīng)用中可以靈活掌握,如下代碼:
Bitmap newimg = new Bitmap(100,100); newimg.Save("d:\\test.jpg", System.Drawing.Imaging.ImageFormat.Png);
保存的數(shù)據(jù),顯示在畫布上可采取如下方法:
1、文件型
System.Drawing.Image img2 = new Bitmap(你的文件地址); canvas.DrawImage(img2, 0, 0); MessageBox.Show("顯示文件到畫布成功!");
2、二進制型
byte[] bytes = 你的二進制數(shù)據(jù); 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數(shù)據(jù); 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實現(xiàn)畫筆簽名功能的詳細內(nèi)容,更多關(guān)于C#畫筆簽名的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
圖解如何使用C#創(chuàng)建Windows服務(wù)
本文主要介紹了圖解如何使用C#創(chuàng)建Windows服務(wù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07C#使用DateTime.Now靜態(tài)屬性動態(tài)獲得系統(tǒng)當(dāng)前日期和時間
本文主要介紹了C#使用DateTime.Now靜態(tài)屬性動態(tài)獲得系統(tǒng)當(dāng)前日期和時間,DateTime結(jié)構(gòu)的Now靜態(tài)屬性只是得到一個系統(tǒng)時間對象,該時間對象不會隨著系統(tǒng)時間的變化而變化,如果要動態(tài)顯示系統(tǒng)時間,可以使用計時器間隔地獲取系統(tǒng)時間對象并顯示,感興趣的可以了解一下2024-01-01C# 實現(xiàn)與現(xiàn)有.NET事件橋接簡單實例
這篇文章主要介紹了C# 實現(xiàn)與現(xiàn)有.NET事件橋接簡單實例的相關(guān)資料,需要的朋友可以參考下2017-03-03