从WM_SIZE消息更改表单大小

时间:2012-02-17 22:19:58

标签: c# winforms winapi

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

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

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

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

我可以处理的另一条消息可能在系统实际执行最大化之前发生,因此我可以在手动之前调整窗口大小和显示模式吗?

当前代码:

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。顶部图像显示扩展显示,对窗口大小没有限制,底部图像显示最小显示,其中设置了高度限制,因此它们无法增加窗口的高度,因为它只会显示更多空空间。

感谢。

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

在我看来,你似乎试图以过于复杂的方式做一件简单的事情。我将处理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.PtrToStructureMessage.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负责其余部分。

答案 2 :(得分:1)

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

在属性中设置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);
}

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