【问题标题】:How can I open a window's system menu by code?如何通过代码打开窗口的系统菜单?
【发布时间】:2008-09-26 14:22:26
【问题描述】:

我有一个 C# WinForms 无边框窗口,为此我重写了 WndProc 并处理 WM_NCHITTEST 消息。对于该表单的区域,我的命中测试函数返回 HTSYSMENU。双击该区域成功关闭表单,但右键单击它不会显示窗口的系统菜单,也不会在右键单击任务栏中的窗口名称时显示。

此表单使用以下样式:

this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
this.SetStyle( ControlStyles.UserPaint, true );
this.SetStyle( ControlStyles.OptimizedDoubleBuffer, true );
this.SetStyle( ControlStyles.ResizeRedraw, true );

并且具有这些非默认属性值:

this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.KeyPreview = true;
this.MaximizeBox = false;
this.MinimizeBox = false;

我尝试过处理 WM_NCRBUTTONDOWN 和 WM_NCRBUTTONUP,并发送 WM_GETSYSMENU 消息,但没有成功。

【问题讨论】:

    标签: winforms winapi


    【解决方案1】:

    如果我没记错的话,无边框窗口会被标记为不提供系统菜单,并且不会出现在任务栏中。

    任何给定窗口都没有边框并且没有出现在任务栏中的事实是窗口上设置的样式标志的结果。这些特定的样式标志可以使用GetWindowLongSetWindowLong API 调用来设置。但是,您必须小心,因为某些样式不能一起使用。

    这些年来,我编写了许多自定义控件,并且我不断地哄着窗口成为它们原本不打算成为的东西。
    例如,我编写了自己的下拉控件,我需要一个窗口来充当弹出窗口而不是激活。
    下面的代码将做到这一点。请注意,代码出现在OnHandleCreated 事件处理程序中。这是因为在设置句柄后需要更改标志,这表明 Windows 已经设置了它认为应该设置的标志。

    using System.Runtime.InteropServices;
    
    protected override void OnHandleCreated(EventArgs e) {
        uint dwWindowProperty;
    
        User32.SetParent(this.Handle, IntPtr.Zero);
    
        dwWindowProperty = User32.GetWindowLong( this.Handle, User32.GWL.EXSTYLE );
        dwWindowProperty = dwWindowProperty | (uint)User32.WSEX.TOOLWINDOW | (uint)User32.WSEX.NOACTIVATE;
        User32.SetWindowLong( this.Handle, User32.GWL.EXSTYLE, dwWindowProperty );
    
        dwWindowProperty = User32.GetWindowLong( this.Handle, User32.GWL.STYLE );
        dwWindowProperty = ( dwWindowProperty & ~(uint)User32.WS.CHILD ) | (uint)User32.WS.POPUP; 
        User32.SetWindowLong( this.Handle, User32.GWL.STYLE, dwWindowProperty );
        base.OnHandleCreated (e);
    }
    
    
    //this is a fragment of my User32 library wrapper needed for the previous code segment.
    class User32 
    {
        [DllImport("user32.dll", SetLastError = true)]
       static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
    
        [DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall )]
        public  static extern int SetWindowLong( IntPtr hWnd, User32.GWL gwlIndex, uint dwNewLong); 
    
        [DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall )]
        public static extern uint GetWindowLong( IntPtr hWnd, User32.GWL gwlIndex );
    
        [FlagsAttribute] 
        public enum WS: uint { 
            POPUP = 0x80000000,
            CHILD = 0x40000000,
        }
    
        public enum GWL {
            STYLE   = -16,
            EXSTYLE = -20
        }
    
        [FlagsAttribute]
        public enum WSEX: uint {
            TOP        = 0x0,
            TOPMOST    = 0x8,
            TOOLWINDOW = 0x80,
            NOACTIVATE = 0x08000000,
        }
    }
    

    很遗憾,如果不使用 Caption 样式,就无法设置 SysMenu 样式,所以我不能说这是否是您的实现中的问题。

    您可以在这两个链接查看原始样式列表和扩展样式列表:
    Window Styles
    CreateWindowEx

    【讨论】:

    • "WS_SYSMENU : 创建一个在其标题栏上有一个窗口菜单的窗口。还必须指定 WS_CAPTION 样式。"是的,就是这样。这是一种无边界和无标题的形式。
    【解决方案2】:

    我的应用程序中具有相同的属性,并且右键单击也不起作用,所以这不是您的问题,这似乎是 Windows 窗体在没有边框时响应的方式。

    如果您将边框设置为正常值,您将能够右键单击任务栏等。

    要右键单击其他控件,您需要设置 ContextMenuStrip 并提供您的“菜单”。但我不确定当你没有边界时这是否有效。我一直无法让它工作。

    【讨论】:

      【解决方案3】:
          protected override void WndProc( ref System.Windows.Forms.Message m )
          { // RightClickMenu
              if ( m.Msg == 0x313 )
              {
                  this.contextMenuStrip1.Show(this, this.PointToClient(new Point(m.LParam.ToInt32())));
              }}
      

      这会检测右键单击应用程序任务栏“区域”..

      也许会有帮助?

      【讨论】:

      • 谢谢,但我不想伪造系统菜单,我正在寻找关于系统菜单(恢复、移动、大小、最小化、最大化、关闭)的答案第一名。
      猜你喜欢
      • 2014-03-16
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 1970-01-01
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多