2
votes

I have a form with a minimum height set, because I don't want it resized beyond a certain point when it is in a "minimalistic display" mode.

When a user attempts to maximize the window by Aero Snapping it to the top of the screen, the window gets maximized, but the height of the window is only 240 pixels (the set maximum size). If I attempt to handle the WM_SIZE message when the wParam is SIZE_MAXIMIZED, any attempt to set the height of the form is bypassed.

Currently I am handling SC_MAXIMIZE to detect when the maximize button is pushed, and WM_NCLBUTTONDBLCLK to maximize the window if the user double clicks the title bar. In both of these situations, I can toggle the extended window mode and set the minimum size such that it will be able to go full screen.

Of course neither of these messages are posted if the window is maximized via ShowWindow(SW_MAXIMIZE) or when aero-snapped to the top of the screen.

Is there another message I can handle that might occur just before the system actually does the maximization so I can adjust the window size and display mode before hand?

Current Code:

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);
}

If the window is already in extended display mode when WM_SIZE maximize is sent, there is no problem because the max window size is set to allow full screen, however, if they attempt to maximize from the minimal mode, I can't get the app to switch to take up the full screen during the message.

I know I could trigger a timer or something to run from the message so it would resize so quickly the user wouldn't notice it wasn't full screen right away, but that is just a terrible hack.

EDIT:

To illustrate the two window states, I have uploaded two screenshots here. The top image shows the extended display, which has no limits on window size, the bottom image shows the minimal display, which has a height restriction set so they can't increase the height of the window as all it would do is show more empty space.

Thanks.

3
Why are you not using the actual window message constant values, like WM_SIZE instead of the hex values?kprobst
No good reason, at first I was only using 2 messages so I used the values instead of defining them in my app as constants like I should.drew010
The concept of "the window height must not exceed Y pixels except when the user wants to make it larger, then it may be larger" is silly, from Windows's perspective. Either the window height is limited to Y pixels, or it isn't. You can't have it both ways.user743382
Since your full screen mode is -- again, from Windows's perspective -- completely different from your minimalistic interface, could you not use two different windows?user743382
You can't make this work, there's no snap notification message. You got into this problem by forcing the user to make your window look pretty. That's just the wrong approach, keep the user in charge.Hans Passant

3 Answers

3
votes

It seems to me like you're trying to do a simple thing in an overly complicated way. I would process the WM_GETMINMAXINFO message, which is sent to your window whenever its size or position is about to change. Handling this message gives you the opportunity to specify maximal or minimal values for each of these attributes, effectively preventing it from being made smaller or larger than you desire.

I won't post a lot of sample code because your question indicates you already know how to override WndProc and handle window messages. The only thing you'll need to do is define the MINMAXINFO structure in managed code. Something like this:

[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;
}

Use Marshal.PtrToStructure to convert the pointer contained in the Message.LParam property to an instance of the MINMAXINFO structure defined above. So, inside of your WndProc method, you'd do something like:

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

Update:

From the screenshots you've posted, it looks like the two different displays are identical, the only difference is whether the DataGridView at the bottom is shown. The GroupBox at the top is shown, regardless of the form's size.

Thus, it seems to me that the solution is simply to handle the Form.Resize event (which should be raised regardless of how your form is resized, whether via manually dragging its borders, clicking the caption bar buttons, or with Aero Snap).

Inside of that event handler method, check the current dimensions of your form. If it is sufficiently large, set the DataGridView control's Visible property to true. If it is not of sufficient size, switch to "minimal mode" by setting DataGridView.Visible = false.

It's not a very technically complicated solution, but it seems like it should accomplish all of your desired goals. The motivation as I understand it is just to provide a simpler interface when the form is too small to be able to see everything, and expand that interface when the form is larger. If you handle the Resize event and check the actual size of the form after that event has fired, you can't be wrong.

An alternative solution is just to enable the AutoScroll property and always show both controls. All the user will have to do is scroll up or down to see whatever they want. WinForms takes care of the rest.

2
votes

Do I understand correctly: you want your window to have minimum allowed size and be able to be maximized? If that is so your code is far too complex and actually not necessary. Just use window's property MinimumSize.

1
votes

So you want your form to have minimum size, maximum size and still be able to maximize it using title bar double click, maximize button and Aero snap? Tricky :-) Here is the solution.

Set you MinimumSize in properties and then write 2 events:

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

and:

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

That will do the trick. At least works on my machine :-)