C# WinForm實(shí)現(xiàn)畫筆簽名功能
需求
我的文章 《C# 結(jié)合JavaScript實(shí)現(xiàn)手寫板簽名并上傳到服務(wù)器》主要介紹了 web 版的需求實(shí)現(xiàn),本文應(yīng)項(xiàng)目需求介紹如何通過 C# WinForm 通過畫布畫筆實(shí)現(xiàn)手寫簽名,并在開發(fā)過程中解決遇到的一些格式轉(zhuǎn)換的問題,提供一些思路。
實(shí)現(xiàn)效果
簽名功能的顯示界面如下圖:

該效果主要實(shí)現(xiàn)如下功能:
1、提供畫布,設(shè)計(jì)畫筆類,實(shí)現(xiàn)畫筆簽名
2、點(diǎn)擊重簽按鈕清空畫布
3、點(diǎn)擊確認(rèn)按鈕保存畫布位圖到指定的格式(提供三種保存類型,文件,二進(jìn)制數(shù)據(jù)和BASE64編碼)
開發(fā)運(yùn)行環(huán)境
操作系統(tǒng): Windows Server 2019 DataCenter
手寫觸屏設(shè)備:Microsoft Surface Pro 9
.net版本: .netFramework4.0 或以上
開發(fā)工具:VS2019 C#
設(shè)計(jì)實(shí)現(xiàn)
界面布局
主要在WinForm上放置如下控件,Name 為 canvasPanel 的 System.Windows.Forms.Panel控件,一些Label控件、radioButton控件和兩個(gè)功能按鈕Button控件,如下圖:

初始化
Form1 初始化如下變量:
bool isMouseDown = false; // 判斷鼠標(biāo)或手指是否按下,按下為 true
Graphics canvas = null; // 定義繪圖畫布
Image bmpData = null; // 定義 Image 圖像,將來導(dǎo)出時(shí)使用實(shí)例化變量的過程中 new Bitmap ,則產(chǎn)生的默認(rèn)格式為 System.Drawing.Imaging.ImageFormat.MemoryBmp 格式,這會產(chǎn)生一個(gè)問題,保存的位圖是全黑色。因此一個(gè)解決的思路是先臨時(shí)創(chuàng)建一個(gè)白色背景的JPEG圖片,圖片的大小取決于panel控件的寬度和高度,然后再將畫布的圖像 bmpData 變量,實(shí)例化創(chuàng)建引用這個(gè)臨時(shí)圖片的路徑。
示例代碼如下:
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)建的畫布,首先定義實(shí)現(xiàn)一個(gè)畫筆類,代碼如下:
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)點(diǎn),并結(jié)合 DrawLine 方法畫出想要的線段 |
| 2 | Color | Color | 畫筆的顏色,默認(rèn)為黑色 |
| 3 | Width | int | 畫筆的粗線,默認(rèn)為2,1為最細(xì) |
實(shí)現(xiàn)繪圖,主要是通過畫筆類,在canvasPanel 的鼠標(biāo)按下、鼠標(biāo)移動(dòng)、和鼠標(biāo)抬起事件定義相關(guān)操作。
| 序號 | 事件名 | 說明 |
|---|---|---|
| 1 | canvasPanel_MouseDown | 記住鼠標(biāo)是否按下,將 bool isMouseDown 置為true,另一個(gè)關(guān)鍵功能是將按下的點(diǎn)(Point),賦值到畫筆的 LastPoint 屬性,以備后續(xù)繪制線條使用 |
| 2 | CanvasPanel_MouseMove | 判斷 isMouseDown 標(biāo)志,如果為 true 則引入畫布圖像,從最后一次的Point結(jié)合當(dāng)前鼠標(biāo)的Point 進(jìn)行 DrawLine 操作,并形成新的位圖數(shù)據(jù) |
| 3 | CanvasPanel_MouseUp | 將 bool isMouseDown 置為 false,不再進(jìn)行繪制 |
示例代碼如下:
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;
}清空畫布
可通過點(diǎn)擊“重簽” 按鈕,清空畫布,實(shí)現(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 選項(xiàng),可以導(dǎo)出三種類型的形式數(shù)據(jù),如下表:
| 序號 | 事件名 | 說明 |
|---|---|---|
| 1 | radioButton1 | 直接導(dǎo)出成文件(jpeg類型) |
| 2 | radioButton2 | 導(dǎo)出二進(jìn)制數(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("已成功保存為二進(jìn)制數(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、二進(jìn)制型
byte[] bytes = 你的二進(jìn)制數(shù)據(jù);
MemoryStream ms = new MemoryStream(bytes);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
canvas.DrawImage(img, 0, 0);
MessageBox.Show("顯示二進(jìn)制到畫布成功!");
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實(shí)現(xiàn)畫筆簽名功能的詳細(xì)內(nèi)容,更多關(guān)于C#畫筆簽名的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
圖解如何使用C#創(chuàng)建Windows服務(wù)
本文主要介紹了圖解如何使用C#創(chuàng)建Windows服務(wù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
c#遠(yuǎn)程html數(shù)據(jù)抓取實(shí)例分享
這篇文章主要介紹了c#遠(yuǎn)程html數(shù)據(jù)抓取的方法,大家參考使用吧2013-12-12
基于C#實(shí)現(xiàn)簡單的隨機(jī)抽獎(jiǎng)小程序
臨近春節(jié),大街小巷的地方都有抽獎(jiǎng)活動(dòng),那么基于C#是如何實(shí)現(xiàn)簡單的抽獎(jiǎng)程序的呢,下面小編給大家分享了具體代碼,有需要的朋友參考下2016-01-01
C#使用DateTime.Now靜態(tài)屬性動(dòng)態(tài)獲得系統(tǒng)當(dāng)前日期和時(shí)間
本文主要介紹了C#使用DateTime.Now靜態(tài)屬性動(dòng)態(tài)獲得系統(tǒng)當(dāng)前日期和時(shí)間,DateTime結(jié)構(gòu)的Now靜態(tài)屬性只是得到一個(gè)系統(tǒng)時(shí)間對象,該時(shí)間對象不會隨著系統(tǒng)時(shí)間的變化而變化,如果要?jiǎng)討B(tài)顯示系統(tǒng)時(shí)間,可以使用計(jì)時(shí)器間隔地獲取系統(tǒng)時(shí)間對象并顯示,感興趣的可以了解一下2024-01-01
C# 實(shí)現(xiàn)與現(xiàn)有.NET事件橋接簡單實(shí)例
這篇文章主要介紹了C# 實(shí)現(xiàn)與現(xiàn)有.NET事件橋接簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03

