C# winform 自定义皮肤制作
最近要做個軟件正在做技術準備,由于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;}elseimgFormMax = (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;elsethis.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;elsethis.WindowState = FormWindowState.Normal;}}?
OK,整個皮膚的制作基本就完成了,乍一看貌似基本功能都實現了,但是如果想實現動態換膚,還是很麻煩的,下篇文章我會給出個動態換膚的解決方案和實現源碼。
就這樣了,歡迎拍磚。(源碼下載)?
轉載于:https://www.cnblogs.com/haiyabtx/archive/2012/09/21/2697190.html
總結
以上是生活随笔為你收集整理的C# winform 自定义皮肤制作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vb 坐标点击
- 下一篇: C# 类(14) 事件