WINFORM自定義皮膚製作(上)
轉自:http://www.cnblogs.com/coding1016/archive/2010/01/22/1653777.html
最近要做個軟體正在做技術準備,由於WINFORM生成的窗體很醜陋,一個好的軟體除了功能性很重要外,UI的體驗也是不容忽視的。習慣性的在網上搜素了下,換膚控制元件也有好幾款,但是有些用起來不是很好用,好點的也要花很多銀子哦,而且畢竟是別人寫的,心裡總不是個滋味,所以決定自己嘗試著寫寫看,花了一個晚上終於做出來了個DEMO,貌似還不錯,貼圖如下(圖片是直接是用的暴風影音的,寒自己一個。。)
下面和大家分享下。
首先分析下皮膚的製作原理,我的理解是把整個窗體(去邊框後)劃分為9個區域(如果有更復雜的介面,可以劃分更多),有圖有真相:
然後準備皮膚素材,切圖,我的切圖如下:
接著可以開工了:
1.初始化圖片資源變數
protected int formMinX = 0;//最小化按鈕的X座標 protected int formMaxX = 0;//最大化按鈕的X座標 protected int formCloseX = 0;//關閉按鈕的X座標 protected int formTitleMarginLeft = 0;//標題欄的左邊界 protected int formTitleMarginRight = 0;//標題欄的右邊界 Image imgTopLeft = (Image)Resources.topleft;//窗體頂部左上角圖片 Image imgTopRight = (Image)Resources.topright;//窗體頂部右上角圖片 Image imgTopMiddle = (Image)Resources.topstretch;//窗體頂部中間圖片 Image imgBottomLeft = (Image)Resources.bottomLeft;//窗體底部左下角圖片 Image imgBottonRight = (Image)Resources.bottomRight;//窗體底部右下角圖片 Image imgBottonmMiddle = (Image)Resources.bottomstretch;//窗體底部中間圖片 Image imgMiddleLeft = (Image)Resources.LeftDrag_Mid;//窗體中部左邊框圖片 Image imgMiddleRight = (Image)Resources.RightDrag_Mid;//窗體中部右邊框圖片 Image imgFormMin = (Image)Resources.skin_btn_min;//最小化按鈕 Image imgFormMax = (Image)Resources.skin_btn_max;//最大化按鈕 Image imgFormClose = (Image)Resources.skin_btn_close;//關閉按鈕 Image imgFormRestore = (Image)Resources.skin_btn_restore;//還原按鈕
2.重寫OnPaint事件。程式碼直接貼上來(比較簡單,就是計算圖片要繪製到窗體的座標,然後把圖片繪到窗體上)
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
this.BackColor = Color.Black;
//繪製皮膚
Graphics g = e.Graphics;
//繪製窗體頂部左上角圖片
g.DrawImage(imgTopLeft, 0, 0, imgTopLeft.Width, imgTopLeft.Height);
int topRightX = e.ClipRectangle.Width - imgTopRight.Width;
//繪製窗體頂部右上角圖片
g.DrawImage(imgTopRight, topRightX, 0, imgTopRight.Width, imgTopRight.Height);
int topMiddleWidth= e.ClipRectangle.Width - (imgTopLeft.Width + imgTopRight.Width) + 4;
//繪製窗體頂部中間圖片(標題欄)
formTitleMarginLeft = imgTopLeft.Width;
formTitleMarginRight = topRightX;
g.DrawImage(imgTopMiddle, imgTopLeft.Width, 0, topMiddleWidth, imgTopMiddle.Height);
//繪製窗體底部左下角圖片
g.DrawImage(imgBottomLeft, 0, e.ClipRectangle.Height - imgBottomLeft.Height, imgBottomLeft.Width, imgBottomLeft.Height);
//繪製窗體底部右下角圖片
g.DrawImage(imgBottonRight, e.ClipRectangle.Width - imgBottomLeft.Width, e.ClipRectangle.Height - imgBottonRight.Height, imgBottonRight.Width, imgBottonRight.Height);
//繪製窗體底部中間圖片
g.DrawImage(imgBottonmMiddle, imgBottomLeft.Width, e.ClipRectangle.Height - imgBottonmMiddle.Height, e.ClipRectangle.Width - (imgBottomLeft.Width + imgBottomLeft.Width) + 4, imgBottonmMiddle.Height);
//畫左右邊框
g.DrawImage(imgMiddleLeft, 0, imgTopLeft.Height, imgMiddleLeft.Width, e.ClipRectangle.Height - (imgTopLeft.Height + imgBottomLeft.Height));
g.DrawImage(imgMiddleRight, e.ClipRectangle.Width - imgMiddleRight.Width, imgTopRight.Height, imgMiddleRight.Width, e.ClipRectangle.Height - (imgTopLeft.Height + imgBottomLeft.Height));
//畫右上角按鈕(最小化,最大化,關閉)
formMinX = topRightX;
g.DrawImage(imgFormMin, topRightX, 0, imgFormMin.Width, imgFormMin.Height);
if (this.WindowState == FormWindowState.Maximized)
{
imgFormMax = imgFormRestore;
}
else
imgFormMax = (Image)Resources.skin_btn_max;
formMaxX = topRightX + imgFormMin.Width;
g.DrawImage(imgFormMax, topRightX + imgFormMin.Width, 0, imgFormMax.Width, imgFormMax.Height);
formCloseX = topRightX + imgFormMax.Width + imgFormMin.Width;
g.DrawImage(imgFormClose, topRightX + imgFormMax.Width + imgFormMin.Width, 0, imgFormClose.Width, imgFormClose.Height);
}
3.當窗體大小發生變化的時候同樣要重繪,所以重寫OnSizeChanged的事件
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
Invalidate();//強制窗體重繪
}
OK,這樣就完成了皮膚的繪製。接下來我們解決的問題有:
1.如何用滑鼠拖拽改變無邊框的大小
2.如何用滑鼠移動無邊框窗體
3.如何讓繪製的最小化,最大化,關閉按鈕執行操作(響應事件)
對於第1個問題拖拽改變無邊框的大小,可以重寫訊息處理函式WndProc(除了這個我找不到其它好的方法了,如果哪個朋友知道請告訴我一聲)
const int WM_NCHITTEST = 0x0084;
const int HTLEFT = 10;
const int HTRIGHT = 11;
const int HTTOP = 12;
const int HTTOPLEFT = 13;
const int HTTOPRIGHT = 14;
const int HTBOTTOM = 15;
const int HTBOTTOMLEFT = 0x10;
const int HTBOTTOMRIGHT = 17;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
Point vPoint = new Point((int)m.LParam & 0xFFFF,
(int)m.LParam >> 16 & 0xFFFF);
vPoint = PointToClient(vPoint);
if (vPoint.X <= 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)HTTOPLEFT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)HTBOTTOMLEFT;
else m.Result = (IntPtr)HTLEFT;
else if (vPoint.X >= ClientSize.Width - 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)HTTOPRIGHT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)HTBOTTOMRIGHT;
else m.Result = (IntPtr)HTRIGHT;
else if (vPoint.Y <= 5)
m.Result = (IntPtr)HTTOP;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)HTBOTTOM;
break;
}
}
第2個問題滑鼠移動無邊框窗體網上一般有三種方法(詳見:[轉]C#無邊框窗體移動的三種方法)
其中有兩種是用WINDOWS訊息機制來完成,但是我發現如果用訊息機制來處理會造成滑鼠的雙擊或者單擊事件不能使用,這一點讓我很糾結,所以就採用了最原始的處理方式。
private Point mouseOffset; //記錄滑鼠指標的座標 private bool isMouseDown = false; //記錄滑鼠按鍵是否按下 private void Main_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { mouseOffset = new Point(-e.X, -e.Y); isMouseDown = true; } }
private void Main_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { isMouseDown = false; } } private void Main_MouseMove(object sender, MouseEventArgs e) { if (isMouseDown) { Point mousePos = Control.MousePosition; mousePos.Offset(mouseOffset.X, mouseOffset.Y); Location = mousePos; } }
第3個問題我的思路是處理滑鼠單擊事件(這就是為什麼我放棄了用訊息處理機制來解決問題2),根據滑鼠的座標位置判斷是在點選哪個圖片來觸發對應的事件
private void Main_MouseClick(object sender, MouseEventArgs e) { //判斷滑鼠是否點的是右上角按鈕區域 if (e.X >= formMinX && e.X <= formMinX + Resources.skin_btn_min.Width && e.Y <= imgFormMin.Height) { this.WindowState = FormWindowState.Minimized; } if (e.X >= formMaxX && e.X <= formMaxX + Resources.skin_btn_max.Width && e.Y <= imgFormMax.Height) { if (this.WindowState != FormWindowState.Maximized) this.WindowState = FormWindowState.Maximized; else this.WindowState = FormWindowState.Normal; } if (e.X >= formCloseX && e.X <= formCloseX + Resources.skin_btn_close.Width && e.Y <= imgFormClose.Height) { Application.Exit(); } }
然後最後的問題就是解決雙擊“標題欄”來最大化或者還原窗體了,我的思路是在繪製窗體的時候,就記錄標題欄的邊界座標值,然後在雙擊事件中,根據滑鼠的座標位置來判斷觸發最大化(還原)事件。
private void Main_MouseDoubleClick(object sender, MouseEventArgs e) { if (e.X >= formTitleMarginLeft && e.X <= formTitleMarginRight && e.Y <= imgTopMiddle.Height) { if (this.WindowState != FormWindowState.Maximized) this.WindowState = FormWindowState.Maximized; else this.WindowState = FormWindowState.Normal; } }
OK,整個皮膚的製作基本就完成了,乍一看貌似基本功能都實現了,但是如果想實現動態換膚,還是很麻煩的,下篇文章我會給出個動態換膚的解決方案和實現原始碼。
就這樣了,歡迎拍磚。(原始碼下載)
相關文章
- 部落格園自定義皮膚
- C#.net winform skin 皮膚大全C#ORM
- 部落格園自定義皮膚工具推薦:awescnb
- 40. 製作勝利和抽卡皮膚
- 手摸手教你設定部落格園自定義皮膚
- jQuery如何製作自定義外掛jQuery
- winform 自定義容器控制元件ORM控制元件
- 後續來啦:Winform/WPF中快速搭建日誌皮膚ORM
- C#自定義控制元件製作篇C#控制元件
- cnetos7 ISO 映象自定義製作
- winform 換膚元件ORM元件
- QT自定義精美換膚介面QT
- 解除紅旗LINUX4.1的一些限制/自定義控制皮膚(轉)Linux
- C#winform製作學生列表C#ORM
- IHS Markit:中國皮膚製造商開始主導大型液晶電視皮膚市場
- Omdia:中國皮膚製造商主導98和100英寸電視皮膚市場
- 小喬皮膚
- 自定義部落格園個人皮膚
- WordPress主題製作進階#10自定義主頁
- 第十七篇:複製控制( 上 ) --- 自定義複製函式函式
- Flex4/AS3.0自定義VideoPlayer元件皮膚,實現Flash視訊播放器FlexS3IDE元件播放器
- 寶塔皮膚如何設定自定義404
- 部落格皮膚
- 用VC++.net製作WinForm Application (轉)C++ORMAPP
- Flutter 自定義繪製 ViewFlutterView
- WinForm 載入自定義控制元件閃爍問題ORM控制元件
- 複製貼上Ctrl+C改為自定義單鍵
- Label--自定義可貼上複製的Label
- 『軟體推薦』搜狗皮膚:精美輸入法皮膚特別定製版
- android自定義View&自定義ViewGroup(上)AndroidView
- 【彙總】 為園友寫的皮膚製作工具 awescnb
- 瀏覽器皮膚瀏覽器
- jsp換皮膚JS
- 本部落格皮膚
- 部落格園皮膚-我的部落格園皮膚設定教程
- winform中可以摺疊的datagridview,自定義控制元件ORMView控制元件
- 如何深度定製 Ubuntu 皮膚的時間日期顯示格式Ubuntu
- QSS定製自定義QSliderIDE