各種“禁用窗口上的關閉按鈕”方法總結及源碼


【實例說明】
 一般情況下,在窗體的右上角都有最大化、最小化和關閉按鈕,丹在MDI窗體中,有事為了避免重復打開同一個窗口,需要禁用窗口上面的“關閉”按鈕,本實例就實現了這樣的功能。
 說道禁用、有的人會說:直接在FormClosing處理不就得了:

1 /// <summary>
2 /// 窗體關閉時的事件
3 /// </summary>
4 private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
5 {
6      e.Cancel = true;    // 取消關閉操作
7 }

當然,這樣可以,但是我們的目標是實現如下圖所示的禁用+變灰:

運行效果如圖所示:
1.png
 很神奇吧、我們知道最大化最小化按鈕時可以禁用的、但是關閉按鈕怎么禁用呢?請看下面的制作過程:

【關鍵技術】
 本實例主要用到了窗口處理方法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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM