C#實際案例分析(第二彈)——By 流星
實驗二
題目要求
構造屬于你的專屬畫圖程序,可參考系統(tǒng)自帶的繪圖板
環(huán)境設置
操作系統(tǒng):Windows 10 x64
SDK:.NET Framework 4.7.2
IDE:Visual Studio 2019
題意分析
題目的要求非常簡單,但是實現(xiàn)起來卻是比較困難的。我們先觀察和分析一下Windows自帶的畫圖板來獲得它們的基本功能,進而自己實現(xiàn)。

可以看到,系統(tǒng)自帶的畫圖板最主要的功能就是這幾個了,我們逐一分析并考慮如何來實現(xiàn)這幾個功能。首先是文件:
?

可以看到它包含了新建畫布,打開,保存,屬性,關于和退出這幾個功能。我們可以通過C#自帶的文件處理或者GDI+自帶的文件功能來完成,詳情請往下看。
接著是畫筆/工具、圖形功能。這些功能在GDI+中都有相應的基礎功能(Pen, Drawline等),我們到時后通過調(diào)用他們來完成。
最后是顏色和畫筆的粗細,這些功能屬于畫圖過程中的屬性,我們需要用一些全局變量來保存它們。通過更改全局變量的值就可以實現(xiàn)不同模式的切換。
接下來我們來分析一下完成這個軟件需要的類。注意,在實際開發(fā)的過程中我們并不需要先研究透這些類,而是在使用的過程中加以查詢即可。

數(shù)據(jù)結構
本題不存在太過復雜的數(shù)據(jù)結構,主要都是調(diào)用C#已有的類來實現(xiàn)功能。具體來說:
Bitmap類:位圖,C#的圖形類,派生自Image類。在這個實驗中我們將在它的實例上作畫。
Pen類:畫圖自然需要畫筆,而Pen的實例就是一支畫筆,它有粗細,顏色等功能,控制Pen實例的移動就能在位圖上畫出線條。
Point類:點,就是電腦屏幕上隱藏著的坐標。GDI+作畫都要依靠坐標來實現(xiàn)。
Color類:顏色,到時候畫圖時使用到的顏色。顏色可使用它已有的內(nèi)涵的枚舉,或是通過ARGB值來指定。(A:不透明度,R, G, B:紅綠藍;ARGB是一個Int32的值,剛好4*8=32位)。
MessageBox類:消息提示框,用于顯示簡單的信息和互動窗口。
ColorDialog類:C#自帶的調(diào)色板功能,可以通過它來實現(xiàn)調(diào)色,保存自定義顏色等。
SaveFileDialog類:C#自帶的保存提示框功能,就是我們平時使用的軟件第一次點擊保存時會彈出來的那種框。
(重頭戲)Graphics類:大名鼎鼎的GDI+,所有的繪圖功能最終都是通過它的實例來實現(xiàn)在窗體或者畫布上作畫。
再次注意,要將這些類的功能全部研究完是非常麻煩的,我們只需要在使用的時候不斷查詢文檔就可以了。

完整代碼
using?System;
using?System.Collections.Generic;
using?System.ComponentModel;
using?System.Data;
using?System.Drawing;
using?System.Linq;
using?System.Text;
using?System.Threading.Tasks;
using?System.Windows.Forms;
namespace?真_畫圖板
{
????public?partial?class?Form1 :?Form
????{
????????#region ?//全局變量
????????Bitmap mypicture;//畫布
????????Point a =?new?Point();
????????Point b =?new?Point();
????????Pen p =?new?Pen(Color.Black,?2);//畫筆
????????Color mainColor =?Color.Black,?subColor =?Color.White;//主副色,供選擇
????????Color canvas =?Color.White;//背景色
????????int?pensize =?1;//筆寬
????????enum?Mode {?BrushMode,?TextMode,?RectangleMode,?OvalMode };//供選擇的工具模式
????????Mode mode =?0;//已選中的工具模式
????????bool?startDrawing =?false;//指示是否開始畫圖
????????bool?created =?false;//指示是否已創(chuàng)建圖像
????????bool?saved =?false;//指示是否保存
????????#endregion
????????#region ?//窗口代碼區(qū)
????????public?Form1()
????????{
????????????InitializeComponent();
????????}
????????private?void?Form1_FormClosing(object?sender,?FormClosingEventArgs e)
????????{
????????????//在已創(chuàng)建圖像而未保存的情況下提醒用戶
????????????if?(created &&?!saved)
????????????{
????????????????if?(MessageBox.Show("圖片未保存,確定要退出程序嗎?",?"",?MessageBoxButtons.OKCancel,?MessageBoxIcon.Information)?==?DialogResult.OK)
????????????????????;
????????????????else
????????????????????e.Cancel?=?true;
????????????}
????????}
????????#endregion
????????#region ?//畫筆代碼區(qū)
????????public?void?RecoverPen()//畫筆復位
????????{
????????????p.Width?=?pensize;
????????????p.Color?=?mainColor;
????????????this.Cursor?=?Cursors.Default;//鼠標箭頭的形態(tài)恢復
????????}
????????private?void?toolStripButton1_Click(object?sender,?EventArgs e)
????????{
????????????//鉛筆
????????????if?(created)
????????????{
????????????????mode =?Mode.BrushMode;
????????????????RecoverPen();
????????????????label1.Text?=?"當前顏色1";
????????????}
????????}
????????private?void?toolStripButton2_Click(object?sender,?EventArgs e)
????????{
????????????//刷子
????????????if?(created)
????????????{
????????????????mode =?Mode.BrushMode;
????????????????RecoverPen();
????????????????p.Width?=?pensize *?2;
????????????????label1.Text?=?"當前顏色1";
????????????}
????????}
????????private?void?toolStripButton3_Click(object?sender,?EventArgs e)
????????{
????????????//橡皮
????????????if?(created)
????????????{
????????????????mode =?Mode.BrushMode;
????????????????p.Color?=?canvas;//背景色,本質(zhì)上就是通過背景色來覆蓋已經(jīng)畫好的線條
????????????????p.Width?=?pensize *?3;//大一點比較明顯
????????????????this.Cursor?=?Cursors.SizeAll;//鼠標形狀換個樣式,目前還沒有找到替換成自定義圖片的方法
????????????}
????????}
????????private?void?toolStripButton4_Click(object?sender,?EventArgs e)
????????{
????????????//文字
????????????if?(created)
????????????{
????????????????mode =?Mode.TextMode;
????????????}
????????}
????????private?void?toolStripButton6_Click(object?sender,?EventArgs e)
????????{
????????????//矩形
????????????if?(created)
????????????{
????????????????mode =?Mode.RectangleMode;
????????????????RecoverPen();
????????????}
????????}
????????private?void?toolStripButton7_Click(object?sender,?EventArgs e)
????????{
????????????//橢圓
????????????if?(created)
????????????{
????????????????mode =?Mode.OvalMode;
????????????????RecoverPen();
????????????}
????????}
????????#endregion
????????#region ?//顏色代碼區(qū)
????????public?Color colorBox()
????????{
????????????ColorDialog colorDialog =?new?ColorDialog();
????????????colorDialog.AllowFullOpen?=?true;
????????????colorDialog.FullOpen?=?true;
????????????colorDialog.Color?=?Color.Black;//設置默認顏色為黑
????????????colorDialog.ShowHelp?=?true;
????????????colorDialog.ShowDialog();//模式窗口
????????????p.Color?=?colorDialog.Color;//獲取選擇后的顏色
????????????return?colorDialog.Color;
????????}
????????private?void?pictureBox2_Click(object?sender,?EventArgs e)
????????{
????????????//單擊切換顏色
????????????p.Color?=?mainColor;
????????????label1.Text?=?"當前顏色1";
????????}
????????private?void?pictureBox2_DoubleClick(object?sender,?EventArgs e)
????????{
????????????//雙擊調(diào)出調(diào)色板
????????????mainColor =?colorBox();
????????????Bitmap temp =?new?Bitmap(pictureBox2.Width,?pictureBox2.Height);
????????????Graphics g =?Graphics.FromImage(temp);
????????????g.Clear(mainColor);
????????????pictureBox2.Image?=?temp;
????????????label1.Text?=?"當前顏色1";
????????}
????????private?void?pictureBox3_Click(object?sender,?EventArgs e)
????????{
????????????p.Color?=?subColor;
????????????label1.Text?=?"當前顏色2";
????????}
????????private?void?pictureBox3_DoubleClick(object?sender,?EventArgs e)
????????{
????????????subColor =?colorBox();
????????????Bitmap temp =?new?Bitmap(pictureBox3.Width,?pictureBox3.Height);
????????????Graphics g =?Graphics.FromImage(temp);
????????????g.Clear(subColor);
????????????pictureBox3.Image?=?temp;
????????????label1.Text?=?"當前顏色2";
????????}
????????#endregion
????????#region ?//粗細代碼區(qū)
????????//通過一個Panel(當然用其他空間也可以)來將4個單選按鈕集合在一起實現(xiàn)唯一的粗細
????????private?void?radioButton1_CheckedChanged(object?sender,?EventArgs e)
????????{
????????????if?(radioButton1.Checked)
????????????{
????????????????pensize =?2;
????????????????p.Width?=?pensize;
????????????}
????????}
????????private?void?radioButton2_CheckedChanged(object?sender,?EventArgs e)
????????{
????????????if?(radioButton2.Checked)
????????????{
????????????????pensize =?4;
????????????????p.Width?=?pensize;
????????????}
????????}
????????private?void?radioButton3_CheckedChanged(object?sender,?EventArgs e)
????????{
????????????if?(radioButton3.Checked)
????????????{
????????????????pensize =?6;
????????????????p.Width?=?pensize;
????????????}
????????}
????????private?void?radioButton4_CheckedChanged(object?sender,?EventArgs e)
????????{
????????????if?(radioButton4.Checked)
????????????{
????????????????pensize =?8;
????????????????p.Width?=?pensize;
????????????}
????????}
????????#endregion
????????#region ?//畫布代碼區(qū)
????????private?void?pictureBox1_MouseDown(object?sender,?MouseEventArgs e)
????????{
????????????//(無論什么模式)確定初始左鍵位置
????????????if?(e.Button?==?MouseButtons.Left)
????????????{
????????????????startDrawing =?true;
????????????????b.X?=?a.X?=?e.X;//b就是初始位置了,在下一次按下鼠標左鍵之前它不會改變
????????????????b.Y?=?a.Y?=?e.Y;
????????????????if?(mode ==?Mode.TextMode)//特殊處理
????????????????{
????????????????????Form2 textBox =?new?Form2();//創(chuàng)建含富文本框的新窗口用于輸入要畫的文字
????????????????????textBox.ShowDialog();
????????????????????Graphics g =?Graphics.FromImage(mypicture);
????????????????????g.DrawString(PublicVal.textString,?new?Font("宋體",?12),?new?SolidBrush(Color.Black),?b);
????????????????????//Drawstring函數(shù)用來將輸入的文本繪制到畫布上,它的第一個參數(shù)就是我們的全局變量
????????????????????pictureBox1.Image?=?mypicture;
????????????????????g.Dispose();
????????????????}
????????????}
????????}
????????private?void?pictureBox1_MouseMove(object?sender,?MouseEventArgs e)
????????{
????????????if?(startDrawing ==?true?&&?e.Button?==?MouseButtons.Left)//確定是左鍵
????????????{
????????????????Graphics g =?Graphics.FromImage(mypicture);
????????????????//調(diào)出初始畫布,這塊畫布是在鼠標移動的每一幀中不停疊加更新的
????????????????switch?(mode)
????????????????{
????????????????????case?(Mode.BrushMode):
????????????????????????{
????????????????????????????g.DrawLine(p,?a.X,?a.Y,?e.X,?e.Y);?break;
????????????????????????????//畫短線模擬連續(xù)移動的過程,這是整個畫圖軟件最精髓的一行代碼
????????????????????????}
????????????????????case?(Mode.RectangleMode):
????????????????????????{
????????????????????????????//將原來的矩形通過背景色覆蓋掉之后再繪制畫筆色的新矩形,以實現(xiàn)動畫的效果,最終確定下來的矩形在MouseUp事件中被確定
????????????????????????????//(存在將已作的圖覆蓋掉的bug)
????????????????????????????g.DrawRectangle(new?Pen(canvas,?pensize),?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);
????????????????????????????g.DrawRectangle(p,?b.X,?b.Y,?e.X?-?b.X,?e.Y?-?b.Y);
????????????????????????????break;
????????????????????????}
????????????????????case?(Mode.OvalMode):
????????????????????????{
????????????????????????????g.DrawEllipse(new?Pen(canvas,?pensize),?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);
????????????????????????????g.DrawEllipse(p,?b.X,?b.Y,?e.X?-?b.X,?e.Y?-?b.Y);
????????????????????????????break;
????????????????????????}
????????????????????default:
????????????????????????break;
????????????????}
????????????????pictureBox1.Image?=?mypicture;//將新畫出來的圖在picturebox中顯示
????????????????g.Dispose();//釋放畫筆資源
????????????????a.X?=?e.X;//更新點位置
????????????????a.Y?=?e.Y;
????????????}
????????}
????????private?void?pictureBox1_MouseUp(object?sender,?MouseEventArgs e)
????????{
????????????//(無論什么模式)確定結束位置
????????????Graphics g =?Graphics.FromImage(mypicture);
????????????switch?(mode)
????????????{
????????????????case?(Mode.RectangleMode):
????????????????????{
????????????????????????g.DrawRectangle(p,?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);?break;
????????????????????}
????????????????case?(Mode.OvalMode):
????????????????????{
????????????????????????g.DrawEllipse(p,?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);?break;
????????????????????}
????????????????default:
????????????????????break;
????????????}
????????????pictureBox1.Image?=?mypicture;
????????????g.Dispose();
????????????startDrawing =?false;
????????}
????????#endregion
????????#region ?//菜單欄代碼區(qū)
????????private?void?新建ToolStripMenuItem_Click(object?sender,?EventArgs e)
????????{
????????????if?(created)
????????????{
????????????????if?(MessageBox.Show("圖片未保存,確定要新建嗎?",?"",?MessageBoxButtons.OKCancel,?MessageBoxIcon.Information)?==?DialogResult.OK)
????????????????????;
????????????}
????????????else
????????????{
????????????????created =?true;
????????????????pictureBox1.Visible?=?true;//在新建完成之后才能將畫布在picturebox中顯示出來
????????????????mypicture =?new?Bitmap(pictureBox1.Width,?pictureBox1.Height);
????????????????Graphics g =?Graphics.FromImage(mypicture);
????????????????g.Clear(canvas);//用背景色清空畫布
????????????????pictureBox1.Image?=?mypicture;
????????????}
????????}
????????private?void?設置背景色ToolStripMenuItem_Click(object?sender,?EventArgs e)
????????{
????????????if?(created &&?!saved)//是否保存當前?
????????????{
????????????????if?(MessageBox.Show("圖片未保存,確定要新建嗎?",?"",?MessageBoxButtons.OKCancel,?MessageBoxIcon.Information)?==?DialogResult.OK)
????????????????????;
????????????????else
????????????????????return;
????????????}
????????????ColorDialog colorDialog =?new?ColorDialog();
????????????colorDialog.AllowFullOpen?=?true;
????????????colorDialog.FullOpen?=?true;
????????????colorDialog.Color?=?Color.Black;
????????????colorDialog.ShowHelp?=?true;
????????????colorDialog.ShowDialog();
????????????canvas =?colorDialog.Color;
????????????mypicture =?new?Bitmap(pictureBox1.Width,?pictureBox1.Height);
????????????Graphics g =?Graphics.FromImage(mypicture);
????????????g.Clear(canvas);
????????????pictureBox1.Image?=?mypicture;
????????????return;
????????}
????????private?void?保存ToolStripMenuItem_Click(object?sender,?EventArgs e)
????????{
????????????if?(!created)
????????????{
????????????????MessageBox.Show("未創(chuàng)建圖像!");
????????????????return;
????????????}
????????????SaveFileDialog save =?new?SaveFileDialog();
????????????//saveBox的詳細功能請咨詢文檔
????????????save.Filter?=?"Jpg 圖片|*.jpg|Bmp 圖片|*.bmp|Gif 圖片|*.gif|Png 圖片|*.png|Wmf ?圖片|*.wmf";
????????????save.FilterIndex?=?1;
????????????save.DefaultExt?=?".jpg";
????????????save.FileName?=?"無標題_picture";
????????????if?(save.ShowDialog()?==?DialogResult.OK)
????????????{
????????????????mypicture.Save(save.FileName);
????????????}
????????????saved =?true;
????????}
????????private?void?退出ToolStripMenuItem_Click(object?sender,?EventArgs e)
????????{
????????????//關閉當前窗口
????????????this.Close();
????????}
????????private?void?幫助ToolStripMenuItem_Click(object?sender,?EventArgs e)
????????{
????????????//彈出幫助信息框
????????????Form3 help =?new?Form3();
????????????help.ShowDialog();
????????}
????????private?void?關于ToolStripMenuItem_Click(object?sender,?EventArgs e)
????????{
????????????//彈出作者的信息
????????????Form4 about =?new?Form4();
????????????
????????????about.ShowDialog();
????????}
????????#endregion
????}
????//全局變量區(qū)
????public?class?PublicVal
????{
????????public?static?string?textString =?"";
????}
}
代碼片段分析
全局變量模塊
Bitmap mypicture;//畫布
Point a =?new?Point();
Point b =?new?Point();
Pen p =?new?Pen(Color.Black,?2);//畫筆
Color mainColor =?Color.Black,?subColor =?Color.White;//主副色,供選擇
Color canvas =?Color.White;//背景色
int?pensize =?1;//筆寬
enum?Mode {?BrushMode,?TextMode,?RectangleMode,?OvalMode };//供選擇的工具模式
Mode mode =?0;//已選中的工具模式
bool?startDrawing =?false;//指示是否開始畫圖
bool?created =?false;//指示是否已創(chuàng)建圖像
bool?saved =?false;//指示是否保存
//這個類在最后
public?class?PublicVal
{
????public?static?string?textString =?"";
}
這里解釋一下點a和b。點a是較為通用的點,在畫筆移動的過程中需要不停被更替。而點b則是記錄鼠標點擊畫布后的那個位置,不會輕易更變除非一次繪畫(一筆)結束。
最后的全局變量用于富文本框窗口和主窗口之間的信息傳遞,只會在文字模式中用到,到時候我們再來看。需要提到的是,由于C#中不存在實際上的全局變量,于是我們通過同一個命名空間中的公有類的靜態(tài)變量來實現(xiàn)相應的功能。
畫筆模塊(以最普通的鉛筆為例)
public?void?RecoverPen()//畫筆復位
{
????p.Width?=?pensize;
????p.Color?=?mainColor;
????this.Cursor?=?Cursors.Default;//鼠標箭頭的形態(tài)恢復
}
private?void?toolStripButton1_Click(object?sender,?EventArgs e)
{
????//鉛筆
????if?(created)
????{
????????mode =?Mode.BrushMode;
????????RecoverPen();
????????label1.Text?=?"當前顏色1";
????}
}
在每次狀態(tài)變更回來之后(比如切換到了橡皮擦再切換回鉛筆,其他工具同理)都需要將畫筆恢復到最基本的狀態(tài)。選中工具之后,將模式mode切換到相應的工具,并通過畫布模塊來進行作畫。
調(diào)色板模塊(以主色/顏色1為例)
public?Color colorBox()
{
????ColorDialog colorDialog =?new?ColorDialog();
????colorDialog.AllowFullOpen?=?true;
????colorDialog.FullOpen?=?true;
????colorDialog.Color?=?Color.Black;//設置默認顏色為黑
????colorDialog.ShowHelp?=?true;
????colorDialog.ShowDialog();//模式窗口
????p.Color?=?colorDialog.Color;//獲取選擇后的顏色
????return?colorDialog.Color;
}
private?void?pictureBox2_Click(object?sender,?EventArgs e)
{
????//單擊切換顏色
????p.Color?=?mainColor;
????label1.Text?=?"當前顏色1";
}
private?void?pictureBox2_DoubleClick(object?sender,?EventArgs e)
{
????//雙擊調(diào)出調(diào)色板
????mainColor =?colorBox();
????Bitmap temp =?new?Bitmap(pictureBox2.Width,?pictureBox2.Height);
????Graphics g =?Graphics.FromImage(temp);
????g.Clear(mainColor);
????pictureBox2.Image?=?temp;
????label1.Text?=?"當前顏色1";
}
從這里就可以看出ColorDialog的用途了,最重要的就是Color屬性,它代表了用戶對調(diào)色器操作之后的顏色。畫圖板的界面如下:

點擊確定之后Color屬性就被改變了。
畫布模塊區(qū)(最主要的功能)
private?void?pictureBox1_MouseDown(object?sender,?MouseEventArgs e)
????????{
????????????//(無論什么模式)確定初始左鍵位置
????????????if?(e.Button?==?MouseButtons.Left)
????????????{
????????????????startDrawing =?true;
????????????????b.X?=?a.X?=?e.X;//b就是初始位置了,在下一次按下鼠標左鍵之前它不會改變
????????????????b.Y?=?a.Y?=?e.Y;
????????????????if?(mode ==?Mode.TextMode)//特殊處理
????????????????{
????????????????????Form2 textBox =?new?Form2();//創(chuàng)建一個含富文本框的新窗口用于輸入要畫的文字
????????????????????textBox.ShowDialog();
????????????????????Graphics g =?Graphics.FromImage(mypicture);
????????????????????g.DrawString(PublicVal.textString,?new?Font("宋體",?12),?new?SolidBrush(Color.Black),?b);
????????????????????//Drawstring函數(shù)用來將輸入的文本繪制到畫布上,它的第一個參數(shù)就是我們的全局變量
????????????????????pictureBox1.Image?=?mypicture;
????????????????????g.Dispose();
????????????????}
????????????}
????????}
????????private?void?pictureBox1_MouseMove(object?sender,?MouseEventArgs e)
????????{
????????????if?(startDrawing ==?true?&&?e.Button?==?MouseButtons.Left)//確定是左鍵
????????????{
????????????????Graphics g =?Graphics.FromImage(mypicture);
????????????????//調(diào)出初始畫布,這塊畫布是在鼠標移動的每一幀中不停疊加更新的
????????????????switch?(mode)
????????????????{
????????????????????case?(Mode.BrushMode):
????????????????????????{
????????????????????????????g.DrawLine(p,?a.X,?a.Y,?e.X,?e.Y);?break;
????????????????????????????//畫短線模擬連續(xù)移動的過程,這是整個畫圖軟件最精髓的一行代碼
????????????????????????}
????????????????????case?(Mode.RectangleMode):
????????????????????????{
????????????????????????????//將原來的矩形通過背景色覆蓋掉之后再繪制畫筆色的新矩形,以實現(xiàn)動畫的效果,最終確定下來的矩形在MouseUp事件中被確定
????????????????????????????//(存在將已作的圖覆蓋掉的bug)
????????????????????????????g.DrawRectangle(new?Pen(canvas,?pensize),?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);
????????????????????????????g.DrawRectangle(p,?b.X,?b.Y,?e.X?-?b.X,?e.Y?-?b.Y);
????????????????????????????break;
????????????????????????}
????????????????????case?(Mode.OvalMode):
????????????????????????{
????????????????????????????g.DrawEllipse(new?Pen(canvas,?pensize),?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);
????????????????????????????g.DrawEllipse(p,?b.X,?b.Y,?e.X?-?b.X,?e.Y?-?b.Y);
????????????????????????????break;
????????????????????????}
????????????????????default:
????????????????????????break;
????????????????}
????????????????pictureBox1.Image?=?mypicture;//將新畫出來的圖在picturebox中顯示
????????????????g.Dispose();//釋放畫筆資源
????????????????a.X?=?e.X;//更新點位置
????????????????a.Y?=?e.Y;
????????????}
????????}
????????private?void?pictureBox1_MouseUp(object?sender,?MouseEventArgs e)
????????{
????????????//(無論什么模式)確定結束位置
????????????Graphics g =?Graphics.FromImage(mypicture);
????????????switch?(mode)
????????????{
????????????????case?(Mode.RectangleMode):
????????????????????{
????????????????????????g.DrawRectangle(p,?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);?break;
????????????????????}
????????????????case?(Mode.OvalMode):
????????????????????{
????????????????????????g.DrawEllipse(p,?b.X,?b.Y,?a.X?-?b.X,?a.Y?-?b.Y);?break;
????????????????????}
????????????????default:
????????????????????break;
????????????}
????????????pictureBox1.Image?=?mypicture;
????????????g.Dispose();
????????????startDrawing =?false;
????????}
整個畫圖軟件最精髓最核心的思想就是通過確定點的坐標,調(diào)用GDI+的各種函數(shù)來實現(xiàn)繪制各種圖形。最簡單的畫筆功能就是在鼠標移動的過程中不斷定位點,并在新點和上一個舊點之間繪制短線段來模擬連續(xù)的畫筆移動過程。也就是這一行:g.DrawLine(p, a.X, a.Y, e.X, e.Y);
由于“畫文字”的特殊性(鼠標一按下去不用拖動就要插入),故實現(xiàn)文字的功能被挪到了按下的事件里而非移動事件。在文字模式下按下鼠標,彈出一個含富文本框的窗口,在富文本框中可以輸入自己的內(nèi)容。點擊確定之后,富文本框的text屬性被傳到全局變量中,之后通過DrawString函數(shù)繪制在畫布上。


總結
相比于市面上已有的畫圖板,自己開發(fā)的畫圖板還是存在不少漏洞,比如為了實現(xiàn)畫矩形和橢圓的動態(tài)效果而對已有的畫有擦除的bug。但是總的來說基本的要求還是實現(xiàn)了。同時還有許多功能沒有完善,譬如更換背景圖片,拉框選擇圖片的區(qū)域,放大縮小旋轉等。這些較為復雜的功能將在日后不斷完善的過程中實現(xiàn)。
通過本次實驗,我們能了解到GDI+的基本用法、C#自帶控件如SaveFileDialog, ColorDialog等的使用方法,進一步加深了C#圖形化編程的技術。

參考文獻
李春葆,曾平,喻丹丹.C#程序設計教程(第3版):清華大學出版社,2015
Copyright @ 2021, Bilibili: ForeverMeteor, all rights reserved.?