C#動態(tài)圖像按鈕創(chuàng)建步驟
前一陣子參與一個項目開發(fā),好友總是抱怨工具欄按鈕太簡陋了:要是弄得炫一點該多好啊,看看人家mac的桌面工具欄!事實也的確如此,現(xiàn)在越來越多的程序界面做的是相當(dāng)?shù)目岚?,無論是什么平臺mac、windows還是linux,其界面元素越來越豐富,一個小小的button背景都要在鼠標(biāo)的不同事件激活下漸變來漸變?nèi)ァ5?,遍歷VS IDE中默認(rèn)的工具集合我們是無論如何也找不到如此復(fù)雜的控件的,所以,我們必須借助強(qiáng)大的.Net框架以及GDI+自己動手來制作類似的工具欄按鈕控件。
我們要創(chuàng)建的控件簡單說就是一個動態(tài)漸變的C#動態(tài)圖像按鈕
這個button控件依舊繼承自UserControl,UserControl是制作自定義UI控件絕對的父類,這里不再細(xì)說。我們?yōu)檫@個button取名為DynamicImageButton。制作圖像按鈕當(dāng)然離不開繪制,所以還得用到GDI+,我曾經(jīng)寫過多篇關(guān)于界面元素的文章,比如”利用.Net繪圖技術(shù)制作水晶按鈕控件”、” 利用C#實現(xiàn)任務(wù)欄通知窗口”、” 利用C#為數(shù)碼照片添加拍照日期”、” C#實現(xiàn)運行時拖動控件并調(diào)整控件大小”等等,其中都會涉及到GDI+的諸多方面,可見GDI+在設(shè)計制作UI上是多么的重要??!
對于按鈕圖片透明度漸變的操作則比較有技巧,采取了個人認(rèn)為比較另類卻極其高效的方法。制作圖片按鈕肯定是要為這個button賦值一個圖像文件的,我們需要公開一個屬性,代碼如下:
- public Bitmap Image
- {
- get { return bmp[0]; }
- set
- {
- bmp[0] = value;
- bmp[1] = returnAlpha(value, 60);
- bmp[2] = returnAlpha(value, 120); ;
- bmp[3] = returnAlpha(value, 180); ;
- draw();
- }
- }
大家注意到,當(dāng)給這個DynamicImageButton的 image屬性賦值一幅圖片后,立即就會對這個原始圖片經(jīng)過4種不同的alpha過濾后分別存放到bmp位圖數(shù)組下。bmp[0]保存原始圖像,bmp[3]的圖像則最透明。這就是本程序的特點所在,也就是在運行時是不進(jìn)行圖像透明度漸變計算的,在給image屬性賦值時計算工作同時也已經(jīng)完成了,這樣可以省下鼠標(biāo)移動事件的巨大計算量。
returnAlpha方法就是將原始圖像中的每一個像素按照相應(yīng)的alpha值進(jìn)行重新繪制后保存在bmp數(shù)組中,不同透明度的圖像作為bmp數(shù)組的不同元素進(jìn)行保存。代碼如下:
- public static Bitmap returnAlpha(Bitmap bmp, int alpha)
- {
- Color col;
- Bitmap bmp2 = new Bitmap(bmp);
- for (int i = 0; i < bmp.Width; i++)
- for (int j = 0; j < bmp.Height; j++)
- {
- col = bmp.GetPixel(i, j);
- if (col.A > 0)
- bmp2.SetPixel(i, j, Color.FromArgb(min(col.A - alpha), col.R, col.G, col.B));
- }
- return bmp2;
- }
到這里大家可能就已經(jīng)明白我的用意了,沒錯!從原始圖像到最終圖像的透明漸變我只設(shè)計了4幀!其實,這對于一個小小的button動畫來說已經(jīng)完全足夠了。對于圖像的繪制方法我們?nèi)耘f采用雙緩沖區(qū)繪制,也就是內(nèi)存復(fù)制,實際上就是雙bitmap對象交替使用,這樣可以更好的防止圖像閃爍(參見我的另一篇文章” .NET框架下使用雙緩沖技術(shù)繪圖”)。相關(guān)代碼如下:
- private void DynamicImageButton_Paint(object sender, System.EventArgs e)
- {
- g2 = Graphics.FromImage(dblbuffer);
- g2.Clear(this.BackColor);
- curx = (int)((double)Width) / 6;
- cury = (int)((double)Height) / 6;
- curwidth = (int)((double)Width) / 3 * 2;
- curheight = (int)((double)Height) / 3 * 2;
- itvwidth = (Width - curwidth) / 2;
- g2.DrawImage(bmp[3], curx, cury, curwidth, curheight);
- g.DrawImageUnscaled(dblbuffer, 0, 0);
- }
然后就是對C#動態(tài)圖像按鈕大小漸變的控制了,如下圖所示:

a、b、c和d四個矩形代表不同大小的4個幀,a幀是裝載圖像時的默認(rèn)大小,就是bmp[0]的圖像,也是4幀中***的一幀,width=75和 height=72是我們示例程序控件的大小,網(wǎng)友可以隨意對長寬進(jìn)行重新設(shè)定。這兩個數(shù)值是基礎(chǔ),bmp[0]的圖像會完全填充到這個區(qū)域內(nèi),在這個給定的長和寬的基礎(chǔ)上我們計算出b、c和d三幀的圖像大小和位置。然后創(chuàng)建一個計數(shù)器,當(dāng)鼠標(biāo)Enter或者Leave我們創(chuàng)建的這個 DynamicImageButton時對圖像的透明度和大小的漸變進(jìn)行控制。相關(guān)代碼如下:
- private void timer1_Tick(object sender, EventArgs e)
- {
- if (mp == enumMousePosition.Enter)
- {
- if ((curx <= 0) || (cury <= 0) || (Width == curwidth) || (Height == curheight))
- {
- return;
- }
- g2 = Graphics.FromImage(dblbuffer);
- g2.Clear(this.BackColor);
- //g2.Clear(Color.White);
- if (curx >= itvwidth-2)
- {
- g2.DrawImage(bmp[3], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- else if ((curx>= itvwidth / 3 * 2) && (curx < itvwidth))
- {
- g2.DrawImage(bmp[2], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- else if ((curx >= itvwidth / 3) && (curx <= itvwidth / 3 * 2))
- {
- g2.DrawImage(bmp[1], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- else if ((curx >= 0) && (curx <= itvwidth / 3))
- {
- g2.DrawImage(bmp[0], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- g.DrawImageUnscaled(dblbuffer, 0, 0);
- curx--;
- cury--;
- curwidthcurwidth = curwidth + 2;
- curheightcurheight = curheight + 2;
- if ((curx <= 0) || (cury <= 0) || (Width == curwidth) || (Height == curheight))
- {
- timer1.Stop();
- }
- }
- else if (mp == enumMousePosition.Leave)
- {
- if ((curx >= (int)((double)Width) / 6) ||
- (cury >= (int)((double)Height) / 6) ||
- (curwidth <= (int)((double)Width) / 3 * 2) ||
- (curheight == (int)((double)Height) / 3 * 2))
- {
- return;
- }
- g2 = Graphics.FromImage(dblbuffer);
- g2.Clear(this.BackColor);
- //g2.Clear(Color.White);
- if (curx >= itvwidth-2)
- {
- g2.DrawImage(bmp[3], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- else if ((curx >= itvwidth / 3 * 2) && (curx < itvwidth))
- {
- g2.DrawImage(bmp[2], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- else if ((curx >= itvwidth / 3) && (curx <= itvwidth / 3 * 2))
- {
- g2.DrawImage(bmp[1], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- else if ((curx >= 0) && (curx <= itvwidth / 3))
- {
- g2.DrawImage(bmp[0], curx - 1, cury - 1, curwidth + 2, curheight + 2);
- }
- g.DrawImageUnscaled(dblbuffer, 0, 0);
- curx++;
- cury++;
- curwidthcurwidth = curwidth - 2;
- curheightcurheight = curheight - 2;
- if ((curx >= (int)((double)Width) / 6) ||
- (cury >= (int)((double)Height) / 6) ||
- (curwidth <= (int)((double)Width) / 3 * 2) ||
- (curheight == (int)((double)Height) / 3 * 2))
- {
- timer1.Stop();
- }
- }
- }
至此,一個超酷的C#動態(tài)圖像按鈕就制作完畢了,該程序在Windows XP SP3 +VS2005 C# SP1環(huán)境下編譯調(diào)試通過。
【編輯推薦】


















