前幾篇我已經向大家介紹瞭如何使用GDI+來繪圖,並做了一個截圖的實例,這篇我向大家介紹下如何來做一個類似windows畫圖的工具.
個人認為如果想做一個功能強大的繪圖工具,那麼單純掌握GDI還遠遠不夠,我的目前也只能做一個比較簡單的繪圖工具了.不足之處,歡迎大家討論!
先來看一下最終效果吧:
主要實現功能:畫直線,矩形,橡皮,圓形,切換顏色,打開圖片,保存圖片,清除圖片,手動調節畫布大小;軟體剛啟動時,為一張空白畫布,我們可以直接在畫布上繪畫,也可以透過選單中的“開啟”,匯入一張圖片,然後我們就可以在這張圖片上進行繪製。
平台:VS2005 WINFORM
由於程式碼過多,在這裡只簡單介紹下製作步驟,提供大家工程下載.
1.對整個介面進行佈局.
2.實作繪圖工具的功能
3.實作顏色拾取的功能,這裡我們直接拿上次寫的自訂控制項來用.
4.實現選單功能
5.實現手動調節畫布大小的功能
6.測試
實現繪圖工具的功能
為了讓模式稍合度模式,稍微合度還是有點亂亂的,嘿嘿!這些關於繪圖工具的功能塊全部寫在了DrawTools這個類別裡.那麼在主窗體中,只需要呼叫這個類別來完成繪製就行了,而不需要過多的涉及到具體的繪圖程式碼。繪圖工具這個類別提供的主要工具有:鉛筆、橡皮擦、直線、長方形、圓形、實心長方形、實心圓形。關於這些功能塊的程式碼,並不難,只要大家對認真看過前幾篇內容,就應該都看得懂。
這裡要注意以下幾點:
1.如何防止記錄不必要的繪圖過程中的痕跡?
這個問題在第三篇有提到過,大家不妨先去看看那一篇。為了讓程式碼看起來可讀性高點,我設定了兩個Image變量,finishingImg用來保存繪圖過程中的痕跡,orginalImg用來保存已完成的繪圖過程和初始時的背景圖片。
2.這個類別如何與主視窗溝通?
當然如果直接將這些功能塊寫在主窗體中自然沒有這個問題。但那樣程式碼會顯得很混雜,如果只是工具程式碼出現問題就需要改整個專案。我在這裡透過定義方法和屬性,讓主窗體透過給屬性賦值將畫板畫布以及顏色什麼的資訊傳給這個工具類,然後透過呼叫相應的工具方法來使用這些工具。
3.關鍵屬性
要想讓這些工具能正常使用,必須傳遞給他以下幾樣東西:目標畫板(也就是picturebox),繪圖顏色,原始畫布。
實現選單功能
這裡就需要我們對文件的操作有一點了解,大家可以去查一下相關資料。
難點主要就是「開啟」這個選單項目的實作
我們要實現將開啟後的圖片在修改後重新儲存就必須讓檔案在開啟後就能關閉,否則就會因為檔案開啟而無法覆蓋原檔案。就會導致編譯時彈出「GDI 一般性錯誤」。所以根據網路上其它朋友的做法就是先將打開的圖片透過GDI+將圖片畫到另一個畫布上,然後及時關閉打開的圖片和用來繪製該圖片的畫板。
private void openPic_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog();//实例化文件打开对话框 ofd.Filter = "JPG|*.jpg|Bmp|*.bmp|所有文件|*.*";//设置对话框打开文件的括展名 if (ofd.ShowDialog() == DialogResult.OK) { Bitmap bmpformfile = new Bitmap(ofd.FileName);//获取打开的文件 panel2.AutoScrollPosition = new Point(0,0);//将滚动条复位 pbImg.Size = bmpformfile.Size;//调整绘图区大小为图片大小 reSize.Location = new Point(bmpformfile.Width, bmpformfile.Height);//reSize为我用来实现手动调节画布大小用的 //因为我们初始时的空白画布大小有限,"打开"操作可能引起画板大小改变,所以要将画板重新传入工具类 dt.DrawTools_Graphics = pbImg.CreateGraphics(); Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height); Graphics g = Graphics.FromImage(bmp); g.FillRectangle(new SolidBrush(pbImg.BackColor), new Rectangle(0, 0, pbImg.Width, pbImg.Height));//不使用这句话,那么这个bmp的背景就是透明的 g.DrawImage(bmpformfile, 0, 0,bmpformfile.Width,bmpformfile.Height);//将图片画到画板上 g.Dispose();//释放画板所占资源 //不直接使用pbImg.Image = Image.FormFile(ofd.FileName)是因为这样会让图片一直处于打开状态,也就无法保存修改后的图片 bmpformfile.Dispose();//释放图片所占资源 g = pbImg.CreateGraphics(); g.DrawImage(bmp, 0, 0); g.Dispose(); dt.OrginalImg = bmp; bmp.Dispose(); sFileName = ofd.FileName;//储存打开的图片文件的详细路径,用来稍后能覆盖这个文件 ofd.Dispose(); } }
清除圖像其實就是用白色填充整個畫布
其它的都比較簡單,這就不具體講了。
實現手動調整畫布大小
網上有人說使用API,但是個人覺得還是使用其它控件幫忙比較簡單,至少我們還看得懂。
思路:放置一個picturebox1(尺寸為5*5),將它固定在主畫板的右下角,然後改變滑鼠進入時的Cursor為箭頭形狀,設定滑鼠按下移動時的事件,讓該picturebox1 跟隨滑鼠移動。當滑鼠放開時,將主畫板的右下角座標調整為picturebox1的座標。
以下來看下程式碼:
其中的reSize就是我們用來幫忙的picturebox控制
private bool bReSize = false;//是否改变画布大小 private void reSize_MouseDown(object sender, MouseEventArgs e) { bReSize = true;//当鼠标按下时,说明要开始调节大小 } private void reSize_MouseMove(object sender, MouseEventArgs e) { if (bReSize) { reSize.Location = new Point(reSize.Location.X + e.X, reSize.Location.Y + e.Y); } } private void reSize_MouseUp(object sender, MouseEventArgs e) { bReSize = false;//大小改变结束 //调节大小可能造成画板大小超过屏幕区域,所以事先要设置autoScroll为true. //但是滚动条的出现反而增加了我们的难度,因为滚动条上下移动并不会自动帮我们调整图片的坐标。 //这是因为GDI绘图的坐标系不只一个,好像有三个,没有仔细了解,一个是屏幕坐标,一个是客户区坐标,还个是文档坐标。 //滚动条的上下移动改变的是文档的坐标,但是客户区坐标不变,而location属性就属于客户区坐标,所以我们直接计算会出现错误 //这时我们就需要知道文档坐标与客户区坐标的偏移量,这就是AutoScrollPostion可以提供的 pbImg.Size = new Size(reSize.Location.X - (this.panel2.AutoScrollPosition.X), reSize.Location.Y - (this.panel2.AutoScrollPosition.Y)); dt.DrawTools_Graphics = pbImg.CreateGraphics();//因为画板的大小被改变所以必须重新赋值 //另外画布也被改变所以也要重新赋值 Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height); Graphics g = Graphics.FromImage(bmp); g.FillRectangle(new SolidBrush(Color.White), 0, 0, pbImg.Width, pbImg.Height); g.DrawImage(dt.OrginalImg, 0, 0); g.Dispose(); g = pbImg.CreateGraphics(); g.DrawImage(bmp, 0, 0); g.Dispose(); dt.OrginalImg = bmp; bmp.Dispose(); }
效果如下圖(仔細看白色區域的右下角):
效果如下圖(仔細看白色區域的右下角):🎜🎜🎜🎜效果如下圖(仔細看白色區域的右下角):🎜🎜🎜🎜效果如下圖(仔細看白色區域的右下角):🎜🎜🎜🎜此時就可以透過拖曳那個小方塊來調整圖片大小了。
這樣,主要的問題差不多已經解決了,但還是有不足這處,歡迎大家提出寶貴的意見。
更多c# GDI+簡單繪圖(四)相關文章請關注PHP中文網!