【實例說明】
一般情況下,在窗體的右上角都有最大化、最小化和關閉按鈕,丹在MDI窗體中,有事為了避免重復打開同一個窗口,需要禁用窗口上面的“關閉”按鈕,本實例就實現了這樣的功能。
說道禁用、有的人會說:直接在FormClosing處理不就得了:
1 /// <summary> 2 /// 窗體關閉時的事件 3 /// </summary> 4 private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) 5 { 6 e.Cancel = true; // 取消關閉操作 7 }
當然,這樣可以,但是我們的目標是實現如下圖所示的禁用+變灰:
運行效果如圖所示:
很神奇吧、我們知道最大化最小化按鈕時可以禁用的、但是關閉按鈕怎么禁用呢?請看下面的制作過程:
【關鍵技術】
本實例主要用到了窗口處理方法WndProc的重寫方法,在該方法的內部截獲單擊關閉窗口的消息,從而實現禁用“關閉”按鈕的功能。
使用GetSystemMenu()和EnableMenuItem()使“關閉”按鈕變灰色、
其它事件的使用等等。
WndProc方法主要用來處理Windows消息,語法格式如下:
1 [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 2 [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 3 protected virtual void WndProc(ref Message m);
[注:詳細的函數說明請參見源碼中的備注說明。]
【設計過程】
(1)打開Visual Studio,新建WinForm應用程序,將其命名為StopCloseButton。
(2)定義以下成員變量及API聲明:
1 private const int SC_CLOSE = 0xF060; //定義關閉按鈕對應的消息值 2 private const int MF_ENABLED = 0x00000000; //禁用 3 private const int MF_GRAYED = 0x00000001; //變灰 4 private const int MF_DISABLED = 0x00000002; //禁用 5 private const int WM_SYSCOMMAND = 0x0112; // 定義要截獲的消息類型 6 7 [DllImport("user32.dll", EntryPoint = "GetSystemMenu")] 8 private static extern IntPtr GetSystemMenu(IntPtr hWnd, int bRevert); 9 10 [DllImport("User32.dll")] 11 public static extern bool EnableMenuItem(IntPtr hMenu, int uIDEnableItem, int uEnable);
(2)重寫WndProc方法、實現當用戶點擊“關閉”按鈕時無反應的效果:
1 /// <summary> 2 /// 重寫WndProc方法、實現當用戶點擊“關閉”按鈕時無反應的效果 3 /// </summary> 4 /// <param name="m">要處理的Windows消息</param> 5 protected override void WndProc(ref Message m) 6 { 7 if ((m.Msg == WM_SYSCOMMAND) && (int)m.WParam == SC_CLOSE) // 當鼠標單擊“關閉”按鈕時 8 { 9 return; // 不進行任何處理 直接返回 10 } 11 base.WndProc(ref m); // 傳遞下一條消息 12 }
(3)先別急着運行、因為那樣除了結束進程是關不掉的、
所以、給自己留一條后路:放一個按鈕、用於退出、在按鈕的Click事件中寫上退出程序的方法:
1 private void button1_Click(object sender, EventArgs e) 2 { 3 Application.Exit(); 4 }
(4)運行后發現,確實“關閉”按鈕不能點擊了、但是、他也沒有變灰色啊、
那是因為我們還沒有調用EnableMenuItem()呢:
在窗體的構造或Load事件中調用GetSystemMenu()和EnableMenuItem()函數以達到變灰“關閉”按鈕的效果:
1 private void FrmMain_Load(object sender, EventArgs e) 2 { 3 IntPtr hMenu = GetSystemMenu(this.Handle, 0); //得到關閉按鈕 4 EnableMenuItem(hMenu, SC_CLOSE, (MF_DISABLED + MF_GRAYED) | MF_ENABLED); //設置樣式(參數可自定義) 5 }
(5)到此,這個小程序就已經實現完畢了,細心的人會發現以下的小Bug:
雖然“關閉”按鈕已經禁用了、但是、當你點擊以下最大化后、雖然關閉按鈕依舊不能用、但是灰色效果消失了、
那么解決的方案就是禁用掉最大化最小化按鈕、(*^_^*)、
或者在窗體狀態改變的事件里面再次調用以下變灰的函數即可。
程序主要函數注釋如下:
1 /** 2 * 【WndProc定義】 3 * /// <summary> 4 * /// 使用窗口處理方法WndProc的重寫方法 5 * /// 截獲單擊關閉窗口的信息 6 * /// </summary> 7 * /// <param name="m">要處理的Windows消息</param> 8 * [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 9 * [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 10 * protected virtual void WndProc(ref Message m); 11 * */ 12 13 /** 14 * 【GetSystemMenu定義】 15 * /// <summary> 16 * /// 該函數允許應用程序為復制或修改而訪問窗口菜單(系統菜單或控制菜單) 17 * /// 函數原型:HMENU GetSystemMenu(HWND hWnd,BOOL bRevert) 18 * /// 備注: 19 * /// 任何沒有用函數GetSystemMenu來生成自己的窗口菜單拷貝的窗口將接受標准窗口菜單。 20 * /// 窗口某單最初包含的菜單項有多種標識符值,如SC_CLOSE,SC_MOVE和SC_SIZE。/ 21 * /// 窗口菜單上的菜單項發送WM_SYSCOMMAND消息。 22 * /// 所有預定義的窗口菜單項的標識符數大於OxFOOO。如果一個應用程序增加命令到窗口菜單,應該使用小於OxFOOO的標識符數。 23 * /// 自動變灰標准窗口菜單上的菜單項。應用程序通過響應在任何某單顯示之前發送的WM_INITMENU消息來實現選取和變灰。 24 * /// Windows CE環境下,不支持系統菜單,但GetSyemMenu以宏的方式實現,以保持和已存在代碼的兼容性。可以使用該宏的返回菜單句柄使關閉框無效,與在Windows桌面平台上一樣。Windows CE下的返回值沒有其他用處。參數bRevert無用。 25 * /// </summary> 26 * /// <param name="hWnd">擁有窗口菜單拷貝的窗口的句柄</param> 27 * /// <param name="bRevert">指定將執行的操作。如果此參數為FALSE,GetSystemMenu返回當前使用窗口菜單的拷貝的句柄。該拷貝初始時與窗口菜單相同,但可以被修改,如果此參數為TRUE,GetSystemMenu重置窗口菜單到缺省狀態。如果存在先前的窗口菜單,將被銷毀</param> 28 * /// <returns>如果參數bRevert為FALSE,返回值是窗口菜單的拷貝的句柄:如果參數bRevert為TRUE,返回值是NULL</returns> 29 * [DllImport("user32.dll", EntryPoint = "GetSystemMenu")] 30 * private static extern IntPtr GetSystemMenu(IntPtr hWnd, int bRevert); 31 * */ 32 33 /** 34 * 【EnableMenuItem定義】 35 * /// <summary> 36 * /// 允許、禁止或變灰指定的菜單條目 37 * /// 函數原型: 38 * /// BOOL EnableMenuItem( 39 * /// HMENUhMenu, // handle to menu 40 * /// UINTuIDEnableItem, // menu item to enable, disable, or gray 41 * /// UINTuEnable // menu item flags 42 * /// ); 43 * /// 備注: 44 * /// 使菜單項有效、無效或變灰。CreateMenu,InsertMenu,ModifyMenu和LoadMenuIndirect成員函數同時也設置菜單項的狀態(有效、無效、或變灰)。 45 * /// 使用MF_BYPOSITION的值需要應用恰當的CMenu對象。若菜單條的CMenu被使用,那么頂層菜單項(菜單條中的某項)將受影響。如果為了在彈出菜單或嵌套的彈出菜單中通過位置來設置項的狀態,那么應用必須指定彈出菜單的CMenu。 46 * /// 當應用指定了MF_BYCOMMAND標志,那么Windows將檢測所有的屬於CMenu的彈出菜單項。因此,除非當前正在復制菜單項,那么使用菜單條的CMenu是非常有效的。 47 * /// </summary> 48 * /// <param name="hMenu">菜單句柄</param> 49 * /// <param name="uIDEnableItem">欲允許或禁止的一個菜單條目的標識符。如果在wEnable參數中設置了MF_BYCOMMAND標志,這個參數就代表欲改變菜單條目的命令ID。如設置的是MF_BYPOSITION,則這個參數代表菜單條目在菜單中的位置(第一個條目肯定是零)</param> 50 * /// <param name="uEnable">參考ModifyMenu函數中的菜單常數標志定義表,其中列出了允許使用的所有常數。對於這個函數,只能指定下述常數:MF_BYCOMMAND,MF_BYPOSITION,MF_ENABLED,MF_DISABLED以及MF_GRAYED、具體值的含義請看下面的【EnableMenuItem函數中的uEnable各值的含義】</param> 51 * /// <returns>返回值指定的先前狀態菜單項。如果菜單項不存在,返回值是0xffffffff</returns> 52 * [DllImport("User32.dll")] 53 * public static extern bool EnableMenuItem(IntPtr hMenu, int uIDEnableItem, int uEnable); 54 * */ 55 56 /** 57 * 【EnableMenuItem函數中的uEnable各值的含義】 58 * MF_BYCOMMAND 指定參數給出已存在的菜單項的命令ID號。此為缺省值。 59 * MF_BYPOSITION 指定參數給出已存在菜單項的位置。第一項所在的位置是0。 60 * MF_DISABLED 使菜單項無效,以便它不能被選擇,但不變灰。 61 * MF_ENABLED 使菜單項有效,以便它能夠被選擇,並可從變灰的狀態中恢復出來。 62 * MF_GRAYED 使菜單項無效,以便它不能被選擇並同時變灰。 63 * 64 * 注解:如指定的菜單條目依附了一個彈出式菜單,那么整個彈出式菜單都會受到影響 65 * */
程序實例代碼下載請聯系我備注一下【各種“禁用窗口上的關閉按鈕”方法總結及源碼】、
我這沒法插附件、
【打了那么多字很辛苦的,支持一下吧O_O 呵呵】
【來自:[LonelyShadow 博客] http://www.cnblogs.com/LonelyShadow】