當 Form 的 BorderStyle 變成 None 的時候,視窗的雙擊最大化、拖拉視窗位置、拖拉改變視窗大小等等視窗事件都會無效。這時,就需要使用到 “WndProc” 也就是 Windows 視窗訊息接收,來恢復消失的那些視窗功能。
-程式碼 WndProc
以下是 WndProc 所需要的宣告。裡面有好幾個 “Rectangle” 代表的是允許讓使用者點選更改視窗大小的範圍,一共有上、下、左、右。
//Result Message
const int HT_MOVE = 2;
const int HT_LEFT = 10;
const int HT_RIGHT = 11;
const int HT_TOP = 12;
const int HT_BOTTOM = 15;
const int HT_TOPLEFT = 13;
const int HT_TOPRIGHT = 14;
const int HT_BOTTOMLEFT = 16;
const int HT_BOTTOMRIGHT = 17;
//WndProc Message
const int WM_NCHITTEST = 0x84;
const int WM_SIZE = 5;
//設定使用者可拖曳區域
const int BasicDis = 10; //允許使用者可拖曳區域大小
Rectangle Top { get { return new Rectangle(0, 0, this.ClientSize.Width, BasicDis); } }
Rectangle Left { get { return new Rectangle(0, 0, BasicDis, this.ClientSize.Height); } }
Rectangle Bottom { get { return new Rectangle(0, this.ClientSize.Height - BasicDis, this.ClientSize.Width, BasicDis); } }
Rectangle Right { get { return new Rectangle(this.ClientSize.Width - BasicDis, 0, BasicDis, this.ClientSize.Height); } }
Rectangle TopMove { get { return new Rectangle(0, BasicDis, this.ClientSize.Width, TopPanel.Height - BasicDis); } }
Rectangle TopLeft { get { return new Rectangle(0, 0, BasicDis, BasicDis); } }
Rectangle TopRight { get { return new Rectangle(this.ClientSize.Width - BasicDis, 0, BasicDis, BasicDis); } }
Rectangle BottomLeft { get { return new Rectangle(0, this.ClientSize.Height - BasicDis, BasicDis, BasicDis); } }
Rectangle BottomRight { get { return new Rectangle(this.ClientSize.Width - BasicDis, this.ClientSize.Height - BasicDis, BasicDis, BasicDis); } }
這邊使用複寫 WndProc 來擷取並使用 “WM_NCHITTEST” 這個 Windows Messages。
可以看到當 Windows Messages 為 WM_NCHITTEST 的時候可以判斷現在的滑鼠座標是否在剛剛宣告的 Rectangle 範圍裡面,如果有的話就運用 message.Result 來回傳對應的結果代碼。
protected override void WndProc(ref Message message)
{
var cursor = this.PointToClient(Cursor.Position);
if (message.Msg == WM_NCHITTEST) // WM_NCHITTEST
{
if (TopLeft.Contains(cursor)) message.Result = (IntPtr)HT_TOPLEFT;
else if (TopRight.Contains(cursor)) message.Result = (IntPtr)HT_TOPRIGHT;
else if (BottomLeft.Contains(cursor)) message.Result = (IntPtr)HT_BOTTOMLEFT;
else if (BottomRight.Contains(cursor)) message.Result = (IntPtr)HT_BOTTOMRIGHT;
else if (TopMove.Contains(cursor)) message.Result = (IntPtr)HT_MOVE;
else if (Top.Contains(cursor)) message.Result = (IntPtr)HT_TOP;
else if (Left.Contains(cursor)) message.Result = (IntPtr)HT_LEFT;
else if (Right.Contains(cursor)) message.Result = (IntPtr)HT_RIGHT;
else if (Bottom.Contains(cursor)) message.Result = (IntPtr)HT_BOTTOM;
return;
}
// Normal WndProc message
else
base.WndProc(ref message);
}
Windows Messages List 參考網頁: https://wiki.winehq.org/List_Of_Windows_Messages
使用 WndProc 來恢復視窗事件的隱藏問題
如果只是一般單純的 Windows Form 到這邊應該就沒什麼問題了。但如果你的 Form 裡面是含有 Panel、TableLayout等等物件蓋住 Form 的時候,這時候你就會發現剛剛設定的 WndProc 起不了作用,因為我們設定的是 Form 的 WndProc,這時候 Panel 在 Form 的上層,就會以 Panel 的 WndProc 為主。要解決這種窘境的話,就須要複寫 Panel 的 WndProc 來達到該效果:
-程式碼 繼承並複寫 Panel
using System;
using System.Windows.Forms;
namespace NoneReSizeEx
{
public partial class ReSizePanel : Panel
{
private const int WM_NCHITTEST = 0x84;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCHITTEST:
m.Result = (IntPtr)(-1); // HTTRANSPARENT
return;
}
// Do the normal message handling
base.WndProc(ref m);
}
}
}
如果對於不熟悉如何加入”自訂控制項”的話,可以參考這篇文章。
-範例程式
範例中就是四邊都是 Panel,如果對於文章看不懂的可以參考範例裡面的作法喔!
-結語
Form 為 None 時要恢復原本的視窗功能,這個方法我找了很多資料,尤其是有 Panel 的時候,資料更為稀少,希望有幫助到大家,如有任何問題歡迎留言互相增進技術唷!