views:

584

answers:

2

In an application I am developing, I need to be able to make a windows form smaller than the minimum height limit imposed by the operating system (36 px in Vista). I have tried intercepting WM_GETMINMAXINFO and providing my own information to override the OS limits, but this only works for the user. From code I can set the height to a value smaller than the limit, but my change only works until WM_WINDOWPOSCHANGED is posted to the message queue (which happens just after I change the height).

A: 

You mean, aside from using a different OS?

How about "Don't use a form"? How big is this thing you need to display? A pixel? Does it need full Windows Forms functionality?

Now, I don't exactly know how to do the above, but it might be a start for you - think outside of the (bounding) box.

John Saunders
My form contains a ListBox (which contains one or more items) and I am trying to make the form conform to the ListBox's size. When the ListBox contains only one item, the height of the form only needs to be 16 px plus the height of the borders (which varies). In Vista, the total height usually comes out at about 32 px, and so Vista's 36 px minimum height leaves extra whitespace at the bottom of the form (which looks ugly). In XP the situation is the same.
Zach Johnson
+2  A: 

After much experimentation and trial-and-error, I have discovered a solution. I was overriding OnResize and conforming the size of the form to the ListBox in it (see my comment on John Saunders answer). As I mentioned in my question, I noticed that the size of the form regresses after WM_WINDOWPOSCHANGED is sent. Further investigation revealed that the size regression actually begins when WM_WINDOWPOSCHANGING is sent. WM_WINDOWPOSCHANGING is the sister message of WM_WINDOWPOSCHANGED which occurs before the window size actually changes. I don't know why, but for some reason WM_WINDOWPOSCHANGING blindly conforms the size of the form to the OS specified limits (apparently it does not query the window with WM_GETMINMAXINFO). Thus, I needed to intercept WM_WINDOWPOSCHANGING and override it with the size I really wanted. This means that I am no longer conforming the size of the form using OnResize, but instead I am conforming the form size when I receive WM_WINDOWPOSCHANGING. This is even better than OnResize, because there is no associated flicker which occurs when the size is changed and then changed again when the size is conformed during OnResize.

Also, it is necessary to intercept and override WM_GETMINMAXINFO, otherwise, even intercepting WM_WINDOWPOSCHANGING will do you no good.

using System.Runtime.InteropServices;

private const int WM_WINDOWPOSCHANGING = 0x0046;
private const int WM_GETMINMAXINFO = 0x0024;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_WINDOWPOSCHANGING)
    {
        WindowPos windowPos = (WindowPos)m.GetLParam(typeof(WindowPos));

        // Make changes to windowPos

        // Then marshal the changes back to the message
        Marshal.StructureToPtr(windowPos, m.LParam, true);
    }

    base.WndProc(ref m);

    // Make changes to WM_GETMINMAXINFO after it has been handled by the underlying
    // WndProc, so we only need to repopulate the minimum size constraints
    if (m.Msg == WM_GETMINMAXINFO)
    {
        MinMaxInfo minMaxInfo = (MinMaxInfo)m.GetLParam(typeof(MinMaxInfo));
        minMaxInfo.ptMinTrackSize.x = this.MinimumSize.Width;
        minMaxInfo.ptMinTrackSize.y = this.MinimumSize.Height;
        Marshal.StructureToPtr(minMaxInfo, m.LParam, true);
   }
}

struct WindowPos
{
     public IntPtr hwnd;
     public IntPtr hwndInsertAfter;
     public int x;
     public int y;
     public int width;
     public int height;
     public uint flags;
}

struct POINT
{
    public int x;
    public int y;
}

struct MinMaxInfo
{
    public POINT ptReserved;
    public POINT ptMaxSize;
    public POINT ptMaxPosition;
    public POINT ptMinTrackSize;
    public POINT ptMaxTrackSize;
}
Zach Johnson