重繪窗體的工作區的時候我們可以用到OnPaint來實現。而非工作區的繪制有幾種思路
1.直接實現WM_NCCALCSIZE消息繪制
2.把窗體修改成None狀態並攔截WM_NCCALCSIZE修改非工作區的大小實現
3.把窗體修改成None直接做一個模擬的狀態
這三種方法都會有一些弱點,這些弱點也許是我沒有找到對應的解決方法 。
1.在實現后需要修改 base.ControlBox = false,要不然在鼠標經過的時候會出現如下圖的效果

其實我們是要的下面的這種效果

但是這樣做的后果是無法使用任務欄的菜單 ,如果有誰知道這個怎么解決的話留個言,在此謝過了
2.第二種實現的話在最大化最小化的時候,不知道是什么原因會產生窗體自動縮小在WM_NCCALCSIZE定義的標題欄的大小,這個也有一個折中的解決方法,就是重寫SetBoundsCore然后注釋掉里面的內容,但是在這樣的話在Visual Studio里面就無法改變窗體的大小了。 如果這個也有解決方法的話,在此謝過了。
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
//base.SetBoundsCore(x, y, width, height, specified);
}
3.第三種當然是最好實現的了 ,但是模擬的狀態無法直接替換到以前已經寫好的窗體,直接否掉了 。
第一種和第二種其實是有點類似的,主要的地方還是在繪制上面 。
重繪非工作區,這個地方的重點在 DarwRegion函數里的
using (Bitmap img = new Bitmap(this.Bounds.Width, this.Bounds.Height))
{ Graphics g = Graphics.FromImage(img);
……
gs.DrawImage(img, Point.Empty);
}
這里利用雙緩存先在圖片上繪制了然后在畫在窗體上

繪制函數 /// <summary>
/// 重繪非工作區
/// </summary>
private void NCPaint()
{
IntPtr wdc = Common.GetWindowDC(this.Handle);// Graphics.FromHwnd(this.Handle);
Graphics g = Graphics.FromHdc(wdc);
try
{
DarwRegion(g);
}
catch
{ }
Common.ReleaseDC(this.Handle, wdc);
}
private void DarwRegion(Graphics gs)
{
if (this.FormBorderStyle == FormBorderStyle.None)
return;
using (Bitmap img = new Bitmap(this.Bounds.Width, this.Bounds.Height))
{
Graphics g = Graphics.FromImage(img);
if (mouseMove == MouseMose.None)
{
GraphicsPath drawRect = RectX(new Rectangle(0, 0, this.Bounds.Width, this.Bounds.Height), Arc);
//繪制漸變色
Rectangle rect = new Rectangle(0, 0, this.Bounds.Width, this.Bounds.Height);
LinearGradientBrush brush = new LinearGradientBrush(rect, this._colorA, this._colorB, 90);
drawRect.AddRectangle(new Rectangle(BorderWidth, HeadHeight, this.ClientSize.Width, this.ClientSize.Height));
g.FillPath(brush, drawRect);
//繪制圖標Icon
if (ShowIcon)
g.DrawIcon(this.Icon, new Rectangle(BorderWidth + 5, (HeadHeight - 16) / 2, 16, 16));
//繪制標題
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
//求標題欄的區域
ToolTitle = new Rectangle(BorderWidth + 23, 0, this.Bounds.Width, HeadHeight);
g.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), ToolTitle, sf);
//繪制最大最小關閉按鈕
//g.DrawImage(butClose, ButClose);
//g.DrawImage(butMax, ButMax);
//g.DrawImage(butMin, ButMin);
DrawButton(g);
//繪制邊框
g.SmoothingMode = SmoothingMode.HighQuality; //高質量
drawRect = RectX(new Rectangle(1, 1, this.Bounds.Width - 4, this.Bounds.Height - 4), Arc - 1);
g.DrawPath(new Pen(Color.FromArgb(100, Color.White), 1), drawRect);//繪制內邊框
drawRect = RectX(new Rectangle(0, 0, this.Bounds.Width - 2, this.Bounds.Height - 2), Arc - 1);
g.DrawPath(new Pen(SolidColor, 1), drawRect);//繪制外邊框
}
else
{
int i = this.Bounds.Width - 3, h = 1;
foreach (ToolButton a in ListButton)
{
if (!a.Start)
continue;
i = i - a.Size.Width;
Rectangle rect = new Rectangle(new Point(i, h), a.Size);
if (a == _newbutton)
{
g.FillRectangle(new SolidBrush(ColorA), rect);
g.DrawImage(a.Move, rect);
}
else
if (a == _oldbutton)
{
g.FillRectangle(new SolidBrush(ColorA), rect);
g.DrawImage(a.Default, rect);
}
}
}
gs.DrawImage(img, Point.Empty);
}
}
三個按鈕的繪制,可以參考我前一篇重寫TabControl標簽的方法 。
最后是幾個需要重繪的消息

非客戶區刷新 /// <summary>
/// 非客戶區刷新
/// </summary>
private void NCUpdate()
{
Common.SendMessage(this.Handle, (int)Msg.WM_NCPAINT, 0, 0);
}

非客戶區刷新 switch (m.Msg)
{
case (int)Msg.WM_NCCALCSIZE:
BorderWidth = (this.Bounds.Width - this.ClientSize.Width) / 2;
HeadHeight = this.Bounds.Height - this.ClientSize.Height - BorderWidth;
mouseMove = MouseMose.None;
// NCPaint();
base.WndProc(ref m);
break;
case (int)Msg.WM_NCACTIVATE:
base.WndProc(ref m);
mouseMove = MouseMose.None;
NCUpdate();
break;
case (int)Msg.WM_NCPAINT:
NCPaint();
break;
case (int)Msg.WM_NCLBUTTONDOWN:
if (GetMouseButton() == null)
{
base.WndProc(ref m);
// Console.WriteLine(1);
}
break;
case (int)Msg.WM_NCLBUTTONUP:
ToolButton but = GetMouseButton();
if (but != null)
{
but.OnClick();
}
base.WndProc(ref m);
break;
case (int)Msg.WM_NCMOUSEMOVE:
NewButton = GetMouseButton();
base.WndProc(ref m);
break;
case (int)Msg.WM_ERASEBKGND:
base.WndProc(ref m);
NCPaint();
mouseMove = MouseMose.None;
NCUpdate();
break;
default:
base.WndProc(ref m);
break;
}
在這里鼠標點擊關閉按鈕是會出現WM_NCLBUTTONDOWN無法接收到消息,研究了很久發現了一個可以收到的方法,在WM_NCLBUTTONUP事件中判斷是否是在最大最小關閉按鈕按下的,如果是就不執行
base.WndProc(
ref m)這句。
基本上窗體繪制的原理就這么些東西了,如果哪位同學對上面的方法有更好的建議的話,歡迎討論 。