• 各种“禁用窗口上的关闭按钮”方法总结及源码


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

     

    【来自:张董'Blogs:http://www.cnblogs.com/LonelyShadow,转载请注明出处。】

    亲们。码字不容易,觉得不错的话记得点赞哦。。

  • 相关阅读:
    68、成员列表初始化?
    67、类成员初始化方式?构造函数的执行顺序 ?为什么用成员初始化列表会快一 些?
    64、malloc申请的存储空间能用delete释放吗?
    63、new和delete的实现原理, delete是如何知道释放内存的大小的额?
    62、delete p、delete [] p、allocator都有什么作用?
    60、C++模板是什么,你知道底层怎么实现的?
    nyoj--814--又见拦截导弹(动态规划+贪心)
    hdoj--1950--Bridging signals(二分查找+LIS)
    nyoj--214--单调递增子序列(二)(二分查找+LIS)
    hdoj--1010--Tempter of the Bone(搜索+奇偶剪枝)
  • 原文地址:https://www.cnblogs.com/geeksss/p/3518702.html
Copyright © 2020-2023  润新知