參考:http://www.cnblogs.com/zihuatanejo/articles/1392689.htmlphp
GDI+:Graphics Device Interface Plus也就是圖形設備接口,提供了各類豐富的圖形圖像處理功能;在C#.NET中,使用GDI+處理二維(2D)的圖形和圖像,使用DirectX處理三維(3D)的圖形圖像,圖形圖像處理用到的主要命名空間是System . Drawing:提供了對GDI+基本圖形功能的訪問,主要有Graphics類、Bitmap類、從Brush類繼承的類、Font類、Icon類、Image類、Pen類、Color類等.
大概瞭解了什麼是GDI+後,咱們來看一下繪圖要用到的主要工具,要畫圖,確定要畫板吧,在C#中畫板能夠經過Graphics這個類來建立,有了畫板,總得弄個筆什麼之類的吧,否則怎麼畫呀,難不成咱們用手指畫.筆又能夠分好多種類,好比鉛筆,畫刷等.它們的區別主要是鉛筆能夠用來畫線條,而畫刷呢,嘿嘿,本身考慮下.在c#中咱們能夠用Pen,Brush類來實現相似功能.顏料則天然是用Color類了.html
有了工具,咱們就能夠開始動手了!(所需命名空間:using System.Drawing;)
實現效果:在空白窗體中畫基本圖形
準備一個畫板:
建立一個畫板主要有3種方式:
A: 在窗體或控件的Paint事件中直接引用Graphics對象
B: 利用窗體或某個控件的CreateGraphics方法
C: 從繼承自圖像的任何對象建立Graphics對象
此次咱們就先以A爲例:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics; //建立畫板,這裏的畫板是由Form提供的.
}
而後,咱們要只筆:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics; //建立畫板,這裏的畫板是由Form提供的.
Pen p = new Pen(Color.Blue, 2);//定義了一個藍色,寬度爲2的畫筆
}
接下來咱們就能夠來畫畫了.
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics; //建立畫板,這裏的畫板是由Form提供的.
Pen p = new Pen(Color.Blue, 2);//定義了一個藍色,寬度爲的畫筆編程
g.DrawLine(p, 10, 10, 100, 100);//在畫板上畫直線,起始座標爲(10,10),終點座標爲(100,100)
g.DrawRectangle(p, 10, 10, 100, 100);//在畫板上畫矩形,起始座標爲(10,10),寬爲,高爲
g.DrawEllipse(p, 10, 10, 100, 100);//在畫板上畫橢圓,起始座標爲(10,10),外接矩形的寬爲,高爲100
}c#
1.首先咱們來看下上一片中咱們使用過的Pen.
Pen的屬性主要有: Color(顏色),DashCap(短劃線終點形狀),DashStyle(虛線樣式),EndCap(線尾形狀), StartCap(線頭形狀),Width(粗細)等.咱們能夠用Pen 來畫虛線,帶箭頭的直線等
Pen p = new Pen(Color.Blue, 5);//設置筆的粗細爲,顏色爲藍色
Graphics g = this.CreateGraphics();
//畫虛線
p.DashStyle = DashStyle.Dot;//定義虛線的樣式爲點
g.DrawLine(p, 10, 10, 200, 10);
//自定義虛線
p.DashPattern = new float[] { 2, 1 };//設置短劃線和空白部分的數組
g.DrawLine(p, 10, 20, 20, 200, 20);
windows
//畫箭頭,只對不封閉曲線有用
設計模式
p.DashStyle = DashStyle.Solid;//實線
p.EndCap = LineCap.ArrowAnchor;//定義線尾的樣式爲箭頭
g.DrawLine(p, 10, 30, 200, 30);
g.Dispose();
p.Dispose();
2.接下來咱們來看下Brush的使用數組
做用:咱們能夠用畫刷填充各類圖形形狀,如矩形、橢圓、扇形、多邊形和封閉路徑等,主要有幾種不一樣類型的畫刷:ide
? SolidBrush:畫刷最簡單的形式,用純色進行繪製工具
? HatchBrush:相似於 SolidBrush,可是能夠利用該類從大量預設的圖案中選擇繪製時要使用的圖案,而不是純色佈局
? TextureBrush:使用紋理(如圖像)進行繪製
? LinearGradientBrush:使用沿漸變混合的兩種顏色進行繪製
? PathGradientBrush :基於編程者定義的惟一路徑,使用複雜的混合色漸變進行繪製
咱們這裏只是簡單介紹使用其中的幾種:
Graphics g = this.CreateGraphics();
Rectangle rect = new Rectangle(10, 10, 50, 50);//定義矩形,參數爲起點橫縱座標以及其長和寬
//單色填充
SolidBrush b1 = new SolidBrush(Color.Blue);//定義單色畫刷
g.FillRectangle(b1, rect);//填充這個矩形
//字符串
g.DrawString("字符串", new Font("宋體", 10), b1, new PointF(90, 10));
//用圖片填充
TextureBrush b2 = new TextureBrush(Image.FromFile(@"e:picture1.jpg"));
rect.Location = new Point(10, 70);//
rect.Width = 200;//更改這個矩形的寬來
rect.Height = 200;//更改這個矩形的高
g.FillRectangle(b2, rect);
//用漸變色填充
rect.Location = new Point(10, 290);
LinearGradientBrush b3 = new LinearGradientBrush(rect, Color.Yellow , Color.Black , LinearGradientMode.Horizontal);
g.FillRectangle(b3, rect);
3.座標軸變換
在winform中的座標軸和咱們平時接觸的平面直角座標軸不一樣,winform中的座標軸方向徹底相反:窗體的左上角爲原點(0,0),水平向左則X增大,垂直下向則Y增大
接下來,咱們來實際操做下,經過旋轉座標軸的方向來畫出不一樣角度的圖案,或經過更改座標原點的位置來平衡座標軸的位置.
Graphics g = this.CreateGraphics();
//單色填充
//SolidBrush b1 = new SolidBrush(Color.Blue);//定義單色畫刷
Pen p = new Pen(Color.Blue,1);
//轉變座標軸角度
for (int i = 0; i < 90; i++)
{
g.RotateTransform(i);//每旋轉一度就畫一條線
g.DrawLine(p, 0, 0, 100, 0);
g.ResetTransform();//恢復座標軸座標
}
//平移座標軸
g.TranslateTransform(100, 100);
g.DrawLine(p, 0, 0, 100, 0);
g.ResetTransform();
//先平移到指定座標,而後進行度旋轉
g.TranslateTransform(100,200);
for (int i = 0; i < 8; i++)
{
g.RotateTransform(45);
g.DrawLine(p, 0, 0, 100, 0);
}
g.Dispose();
4.最後咱們來看下Graphics這個畫板上咱們還能夠畫什麼
其實咱們上面用到的都是在畫一些簡單的圖形,直線,矩形,扇形,圓孤等,咱們還能夠用它來繪製圖片,這能夠用它的DrawImage方法.這裏我不詳細講解,你們有興趣能夠本身去MSDN瞭解下.咱們後面會講到的截圖就會用到這個方法.
感謝你們的支持,這幾天從早忙到晚,一個字累呀!!!如今挺困的,可是又不習慣這麼早睡覺,哎~~仍是利用這個時間繼續來寫第三篇吧.
前兩篇已經基本向你們介紹了繪圖的基本知識.那麼,我就用咱們上兩篇所學的,作幾個例子.
咱們先來作一個簡單的----仿QQ截圖關於這個的例子其實網上已經有這方面的資料了,可是爲了文章的完整性,仍是以爲有必要講解.
接下來看看這是如何作到的.
思路:聊天窗體上有一個截圖按鈕,點擊按鈕後,程序將整個屏幕畫在一個新的全屏窗體上,而後顯示這個窗體.由於是全屏的窗體,而且隱藏了菜單欄、工具欄等,因此在咱們看來就好像是一個桌面的截圖,而後在這個新窗體上畫矩形,最後保存矩形中的內容並顯示在原來的聊天窗體中.
步驟:
A.新建一個窗體.命名爲Catch.而後設置這個窗體的FormBorderStyle爲None,WindowState爲Maximized.
B.咱們對代碼進行編輯:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace Client
{
public partial class Catch : Form
{
public Catch()
{
InitializeComponent();
}
用戶變量#region 用戶變量
private Point DownPoint = Point.Empty;//記錄鼠標按下座標,用來肯定繪圖起點
private bool CatchFinished = false;//用來表示是否截圖完成
private bool CatchStart = false;//表示截圖開始
private Bitmap originBmp;//用來保存原始圖像
private Rectangle CatchRect;//用來保存截圖的矩形
#endregion
//窗體初始化操做
private void Catch_Load(object sender, EventArgs e)
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.UpdateStyles();
//以上兩句是爲了設置控件樣式爲雙緩衝,這能夠有效減小圖片閃爍的問題,關於這個你們能夠本身去搜索下
originBmp = new Bitmap(this.BackgroundImage);//BackgroundImage爲全屏圖片,咱們另用變量來保存全屏圖片
}
//鼠標右鍵點擊結束截圖
private void Catch_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
//鼠標左鍵按下時動做
private void Catch_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!CatchStart)
{//若是捕捉沒有開始
CatchStart = true;
DownPoint = new Point(e.X, e.Y);//保存鼠標按下座標
}
}
}
private void Catch_MouseMove(object sender, MouseEventArgs e)
{
if (CatchStart)
{//若是捕捉開始
Bitmap destBmp = (Bitmap)originBmp.Clone();//新建一個圖片對象,並讓它與原始圖片相同
Point newPoint = new Point(DownPoint.X, DownPoint.Y);//獲取鼠標的座標
Graphics g = Graphics.FromImage(destBmp);//在剛纔新建的圖片上新建一個畫板
Pen p = new Pen(Color.Blue,1);
int width = Math.Abs(e.X - DownPoint.X), height = Math.Abs(e.Y - DownPoint.Y);//獲取矩形的長和寬
if (e.X < DownPoint.X)
{
newPoint.X = e.X;
}
if (e.Y < DownPoint.Y)
{
newPoint.Y = e.Y;
}
CatchRect = new Rectangle(newPoint,new Size(width,height));//保存矩形
g.DrawRectangle(p,CatchRect);//將矩形畫在這個畫板上
g.Dispose();//釋放目前的這個畫板
p.Dispose();
Graphics g1 = this.CreateGraphics();//從新新建一個Graphics類
//若是以前那個畫板不釋放,而直接g=this.CreateGraphics()這樣的話沒法釋放掉第一次建立的g,由於只是把地址轉到新的g了.如同string同樣
g1 = this.CreateGraphics();//在整個全屏窗體上新建畫板
g1.DrawImage(destBmp,new Point(0,0));//將剛纔所畫的圖片畫到這個窗體上
//這個也能夠屬於二次緩衝技術,若是直接將矩形畫在窗體上,會形成圖片抖動而且會有無數個矩形.
g1.Dispose();
destBmp.Dispose();//要及時釋放,否則內存將會被大量消耗
}
}
private void Catch_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (CatchStart)
{
CatchStart = false;
CatchFinished = true;
}
}
}
//鼠標雙擊事件,若是鼠標位於矩形內,則將矩形內的圖片保存到剪貼板中
private void Catch_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left&&CatchFinished)
{
if (CatchRect.Contains(new Point(e.X, e.Y)))
{
Bitmap CatchedBmp = new Bitmap(CatchRect.Width, CatchRect.Height);//新建一個於矩形等大的空白圖片
Graphics g = Graphics.FromImage(CatchedBmp);
g.DrawImage(originBmp, new Rectangle(0, 0, CatchRect.Width, CatchRect.Height), CatchRect, GraphicsUnit.Pixel);
//把orginBmp中的指定部分按照指定大小畫在畫板上
Clipboard.SetImage(CatchedBmp);//將圖片保存到剪貼板
g.Dispose();
CatchFinished = false;
this.BackgroundImage = originBmp;
CatchedBmp.Dispose();
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
}
}
C.建立了Catch窗體後,咱們在截圖按鈕(位於聊天窗體上)上加入如下事件:
private void bCatch_Click(object sender, EventArgs e)
{if (bCatch_HideCurrent.Checked)
{
this.Hide();//隱藏當前窗體
Thread.Sleep(50);//讓線程睡眠一段時間,窗體消失須要一點時間
Catch CatchForm = new Catch();
Bitmap CatchBmp = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);//新建一個和屏幕大小相同的圖片
Graphics g = Graphics.FromImage(CatchBmp);
g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height));//保存全屏圖片
CatchForm.BackgroundImage = CatchBmp;//將Catch窗體的背景設爲全屏時的圖片
if (CatchForm.ShowDialog() == DialogResult.OK)
{//若是Catch窗體結束,就將剪貼板中的圖片放到信息發送框中
IDataObject iData = Clipboard.GetDataObject();
DataFormats.Format myFormat = DataFormats.GetFormat(DataFormats.Bitmap);
if (iData.GetDataPresent(DataFormats.Bitmap))
{
richtextbox1.Paste(myFormat);
Clipboard.Clear();//清除剪貼板中的對象
}
this.Show();//從新顯示窗體
}
}
}
這樣咱們的截圖功能便完成了.
我想對於初學者來講如何消去第一次繪製的圖片是個比較困難的問題.若是沒有采起措施,你會發現只要你鼠標移動,就會畫一個矩形,這樣便會出現N多的矩形,而咱們只是要最後的那一個.
通常解決這種問題的方法有兩種:
1.就是在繪製第二個圖形時,咱們先用與底色相同的顏色將上次繪製的圖形從新繪製一下.但這每每須要底色爲純色時使用.
2.咱們並不直接將圖形畫在畫板上,咱們用一個圖片A來保存原畫板上的圖片.而後再新建一個與圖片A相同的圖片B,將咱們要繪製的圖形畫在該圖片B上,而後再將該圖片B畫在畫板上.這樣圖片A並無被改變.因而第二次畫的時候咱們仍是一樣新建一個與圖片A相同的圖片進行繪製.那麼上一次的圖形就不會被保留下來.問題也就解決了.
下一次,向你們介紹如何作一個仿windows畫板的程序.
前幾篇我已經向你們介紹瞭如何使用GDI+來繪圖,並作了一個截圖的實例,這篇我向你們介紹下如何來作一個相似windows畫圖的工具.
主要實現功能:畫直線,矩形,橡皮,圓形,切換顏色,打開圖片,保存圖片,清除圖片,手動調節畫布大小;軟件剛啓動時,爲一張空白畫布,咱們能夠直接在畫布上繪圖,也能夠經過菜單中的「打開」,導入一張圖片,而後咱們就能夠在這張圖片上進行繪製。
平臺:VS2005 WINFORM
因爲代碼過多,在這裏只簡要介紹下製做步驟,提供你們工程下載.
1.對整個界面進行佈局.
2.實現繪圖工具的功能
3.實現顏色拾取的功能,這裏咱們直接拿上次寫的自定義控件來用.
4.實現菜單功能
5.實現手動調節畫布大小的功能
6.測試
實現繪圖工具的功能
爲了讓代碼藕合度小點,稍許用了些設計模式,由於不是很會,因此代碼仍是有點亂亂的,繪圖工具的這些功能塊所有寫在了DrawTools這個類裏.那麼在主窗體中,只須要調用這個類來完成繪製就好了,而不須要過多的涉及到具體的繪圖代碼。繪圖工具這個類提供的主要工具就是:鉛筆、橡皮、直線、矩形、圓形、實心矩形、實心圓形。關於這些功能塊的代碼,並不難,只要你們對認真看過前幾篇內容,那應該都看得懂。
這裏有幾點要注意:
1.如何防止記錄沒必要要的繪圖過程當中的痕跡?
這個問題在第三篇中有提到過,你們不妨先去看看那一篇。爲了讓代碼看起來可讀性高點,我設置了兩個Image變量,finishingImg用來保存繪圖過程當中的痕跡,orginalImg用來保存已完成的繪圖過程和初始時的背景圖片。
2.這個類如何與主窗體進行通訊?
固然若是直接將這些功能塊寫在主窗體中天然沒有這個問題。可是那樣代碼會顯得很混雜,若是隻是工具代碼出現問題就須要改整個項目。我在這裏經過定義方法和屬性,讓主窗體經過給屬性賦值將畫板畫布以及顏色什麼的信息傳給這個工具類,而後經過調用相應的工具方法來使用這些工具。
3.關鍵屬性
要想讓這些工具能正常使用,必須傳遞給他如下幾樣東西:目標畫板(也就是picturebox),繪圖顏色,原始畫布。
實現菜單功能
這裏就須要咱們對文件的操做有一點了解,你們能夠去查一下相關資料。
難點主要就是「打開」這個菜單項的實現
咱們要實現將打開後的圖片在修改後從新保存就必須讓文件在打開後就能關閉,不然就會由於文件打開而沒法覆蓋原文件。就會致使編譯時彈出「GDI 通常性錯誤」。因此根據網上其它朋友的作法就是先將打開的圖片經過GDI+將圖片畫到另外一個畫布上,而後及時關閉打開的圖片和用來繪製該圖片的畫板。詳見http://www.wanxin.org/redirect.php?tid=3&goto=lastpost
private void openPic_Click(object sender, EventArgs e)
這裏就須要咱們對文件的操做有一點了解,你們能夠去查一下相關資料。
難點主要就是「打開」這個菜單項的實現
咱們要實現將打開後的圖片在修改後從新保存就必須讓文件在打開後就能關閉,不然就會由於文件打開而沒法覆蓋原文件。就會致使編譯時彈出「GDI 通常性錯誤」。因此根據網上其它朋友的作法就是先將打開的圖片經過GDI+將圖片畫到另外一個畫布上,而後及時關閉打開的圖片和用來繪製該圖片的畫板。詳見http://www.wanxin.org/redirect.php?tid=3&goto=lastpost
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.
//可是滾動條的出現反而增長了咱們的難度,由於滾動條上下移動並不會自動幫咱們調整圖片的座標。
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();
}
此時就能夠經過拖動那個小方塊來調節圖片大小了。