【问题标题】:Changing form size from a WM_SIZE message从 WM_SIZE 消息更改表单大小
【发布时间】:2012-02-17 22:19:57
【问题描述】:

我有一个设置了最小高度的表单,因为当它处于“简约显示”模式时,我不希望它的大小超过某个点。

当用户尝试通过 Aero 将窗口对齐到屏幕顶部来最大化窗口时,窗口会最大化,但窗口的高度仅为 240 像素(设置的最大尺寸)。如果我在wParamSIZE_MAXIMIZED 时尝试处理WM_SIZE 消息,则任何设置表单高度的尝试都会被绕过。

目前我正在处理SC_MAXIMIZE 以检测何时按下最大化按钮,并处理WM_NCLBUTTONDBLCLK 以在用户双击标题栏时最大化窗口。在这两种情况下,我都可以切换扩展窗口模式并设置最小尺寸,使其能够全屏显示。

当然,如果通过ShowWindow(SW_MAXIMIZE) 最大化窗口或当aero 捕捉到屏幕顶部时,这些消息都不会发布。

在系统实际执行最大化操作之前,我是否可以处理另一条消息,以便我可以事先调整窗口大小和显示模式?

当前代码:

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x0112) { // WM_SYSCOMMAND
        if (m.WParam == new IntPtr(0xF030)) { // Maximize event - SC_MAXIMIZE from Winuser.h
            // The window is being maximized
            this.MaximumSize = new Size(9999, 9999);
            ToggleDeviceDisplay(true);
            linkToggleDeviceList.Visible = false;
        }
    } else if (m.Msg == 0x00A3) { // WM_NCLBUTTONDBLCLK - Double clicking on window title bar, min or max
        if (this.WindowState == FormWindowState.Normal) {
            if (grpDeviceList.Visible == false) {
                this.MaximumSize = new Size(9999, 9999);
                ToggleDeviceDisplay(true);
            }
            this.WindowState = FormWindowState.Maximized;
            linkToggleDeviceList.Visible = false;
        } else {
            this.WindowState = FormWindowState.Normal;
            linkToggleDeviceList.Visible = true;
        }
        return;
    } else if (m.Msg == 0x0005) { // WM_SIZE
        if (m.WParam == new IntPtr(0x02)) { // SIZE_MAXIMIZED
            // CANT GET WINDOW TO GO TO FULL-SCREEN FROM HERE
            this.MaximumSize = new Size(9999, 9999);

             // THE LINE BELOW DOESN'T WORK, probably because it is already being sized
            this.Height = Screen.FromHandle(this.Handle).WorkingArea.Size.Height;
        } else if (m.WParam == new IntPtr(0x00)) { // SIZE_RESTORED
            linkToggleDeviceList.Visible = true;
        }
    }
    base.WndProc(ref m);
}

如果在发送 WM_SIZE 最大化时窗口已经处于扩展显示模式,则没有问题,因为最大窗口大小设置为允许全屏,但是,如果他们尝试从最小模式最大化,我不能让应用在消息期间切换到全屏。

我知道我可以触发计时器或从消息中运行的其他东西,这样它就会快速调整大小,用户不会立即注意到它不是全屏的,但这只是一个可怕的黑客攻击。

编辑:

为了说明两种窗口状态,我上传了两张截图here。上图显示了扩展显示,它对窗口大小没有限制,下图显示了最小显示,它设置了高度限制,因此他们不能增加窗口的高度,因为它只会显示更多空空间。

谢谢。

【问题讨论】:

  • 你为什么不使用实际的窗口消息常量值,比如WM_SIZE 而不是十六进制值?
  • 没有充分的理由,起初我只使用 2 条消息,所以我使用了这些值,而不是像我应该在我的应用程序中将它们定义为常量。
  • 从 Windows 的角度来看,“窗口高度不得超过 Y 像素,除非用户想要使其更大,否则它可能会更大”的概念是愚蠢的。窗口高度限制为 Y 像素,或者不是。你不能同时拥有它。
  • 既然你的全屏模式——再一次,从 Windows 的角度来看——与你的简约界面完全不同,你不能使用两个不同的窗口吗?
  • 您无法完成这项工作,没有快照通知消息。您通过强迫用户使您的窗口看起来漂亮而陷入了这个问题。这是错误的做法,让用户负责。

标签: c# winforms winapi


【解决方案1】:

在我看来,您正在尝试以一种过于复杂的方式做一件简单的事情。我会处理WM_GETMINMAXINFO message,只要它的大小或位置即将改变,它就会发送到您的窗口。处理此消息使您有机会为这些属性中的每一个指定最大值或最小值,从而有效地防止它变得比您想要的更小或更大。

我不会发布很多示例代码,因为您的问题表明您已经知道如何覆盖WndProc 并处理窗口消息。您唯一需要做的就是在托管代码中定义MINMAXINFO 结构。像这样的:

[StructLayout(LayoutKind.Sequential)]
struct POINT
{
    public int X;
    public int Y;
}

[StructLayout(LayoutKind.Sequential)]
struct MINMAXINFO
{
    public POINT ptReserved;
    public POINT ptMaxSize;
    public POINT ptMaxPosition;
    public POINT ptMinTrackSize;
    public POINT ptMaxTrackSize;
}

使用Marshal.PtrToStructure 将包含在Message.LParam 属性中的指针转换为上面定义的MINMAXINFO 结构的实例。因此,在您的 WndProc 方法中,您可以执行以下操作:

MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(msg.LParam, typeof(MINMAXINFO));

更新:

从您发布的屏幕截图来看,两个不同的显示看起来是相同的,唯一的区别是底部的 DataGridView 是否显示。无论表单大小如何,都会显示顶部的 GroupBox。

因此,在我看来,解决方案只是处理 Form.Resize 事件(无论是通过手动拖动其边框、单击标题栏按钮,或使用 Aero Snap)。

在该事件处理方法中,检查表单的当前尺寸。如果它足够大,请将DataGridView 控件的Visible 属性设置为true。如果大小不够,请通过设置DataGridView.Visible = false 切换到“最小模式”。

这不是一个技术上非常复杂的解决方案,但它似乎应该可以实现您想要的所有目标。据我了解,其动机只是在表单太小而无法查看所有内容时提供更简单的界面,并在表单较大时扩展该界面。如果您处理Resize 事件并在该事件触发后检查表单的实际大小,您就不会错。

另一种解决方案是启用AutoScroll 属性并始终显示两个控件。用户所要做的就是向上或向下滚动以查看他们想要的任何内容。 WinForms 负责其余的工作。

【讨论】:

  • 不久前我开始尝试WM_GETMINMAXINFO,因为我认为这可能也是可行的方法。现在我遇到的唯一问题是 Windows 在移动窗口或尝试调整窗口大小时发送该消息。如果应用程序处于最小模式,我想说最大尺寸是较小的尺寸,但是当他们尝试航空捕捉时,我需要让它认为最大尺寸是显示器的尺寸。现在,由于移动/调整大小 vs Aero 询问最大大小有多大,我无法弄清楚如何知道消息何时传入。
  • @drew:我不确定我是否理解动机。无论如何,Aero Snap 不会让它比屏幕大。
  • 查看截图here。问题是,在最小模式下,窗口设置了高度限制,aero snap 最大化窗口,但高度仍然只有约 300 像素。我想以某种方式将窗口的大小增加到屏幕大小,并同时启用扩展显示。使用SC_MAXIMIZEWM_NCLBUTTONDBLCLK 效果很好,但是aero snap 仅在最大化我的窗口(使用较小的高度)后才发布WM_SIZE 消息。在 WM_SIZE 中,我似乎无法更改高度,它只是忽略了调整大小。
  • 再次感谢,我最终非常接近,但它显然变得过于复杂,可能不再适用于 Mono,所以我想星期二我会放弃它并使用你的建议之一。
  • @drew010,你试过我的例子吗?它非常简单且有效。
【解决方案2】:

我是否理解正确:您希望您的窗口具有最小允许大小并能够最大化?如果是这样,您的代码就太复杂了,实际上没有必要。只需使用窗口的属性 MinimumSize。

【讨论】:

  • 我确实设置了最小尺寸,但是当窗口处于最小显示模式时,我不希望它能够被调整到超过一定高度,这就是我设置最大尺寸的原因限制。问题是,如果它们处于最小模式,并使用 Aero Snap 最大化窗口,我似乎无法找到在最大化之前增加最大尺寸的方法。
【解决方案3】:

所以您希望您的表单具有最小尺寸、最大尺寸,并且仍然能够使用标题栏双击、最大化按钮和 Aero snap 来最大化它?棘手:-) 这是解决方案。

在属性中设置MinimumSize,然后编写2个事件:

private void Form1_Resize(object sender, EventArgs e)
{
    if (WindowState == FormWindowState.Normal)
    {
        MaximumSize = new Size(maxWidth, maxHeight);
    }
}

和:

private void Form1_ResizeEnd(object sender, EventArgs e)
{
    MaximumSize = new Size(0, 0);
}

这样就可以了。至少可以在我的机器上工作:-)

【讨论】:

    猜你喜欢
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 2017-07-10
    • 2013-02-28
    • 1970-01-01
    • 2018-06-26
    相关资源
    最近更新 更多