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