日期:2014-06-10  浏览次数:20616 次

【实例说明】
 一般情况下,在窗体的右上角都有最大化、最小化和关闭按钮,丹在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