Overcome OS Imposed Windows Form Minimum Size Limit

前端 未结 6 646
耶瑟儿~
耶瑟儿~ 2020-11-29 08:57

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 t

相关标签:
6条回答
  • 2020-11-29 09:15

    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;
    }
    
    0 讨论(0)
  • 2020-11-29 09:20

    Alexey was so close!

        protected override void SetBoundsCore(int x,int y,int width, int height,BoundsSpecified specified)
        {
            base.SetBoundsCore(x, y, this.MinimumSize.Width, this.MinimumSize.Height, specified);
        }
    

    Did the trick for me. I set the Form's Minimum Size to whatever I want the Form's actual Size to be.

    In my project that's all I have to do to get the Form to be tiny, that might be because setting the Minimum size triggers SetBoundsCore or maybe I'm doing something else that triggers it; in which case I guess you have to somehow trigger SetBoundsCore yourself.

    0 讨论(0)
  • 2020-11-29 09:25

    I wish I could give more than +1 to Zach for that, it's great and saved my bacon. For future readers, here's the VB translation of Zach's code:

    Imports System.Runtime.InteropServices
    Imports System.Windows.Forms
    Imports System.Drawing
    
    Public Class MyForm
    
        ' Ghastly hack to allow the form to be narrower than the widows-imposed limit (about 132 in WIndows 7)
        ' Thanks to http://stackoverflow.com/questions/992352/overcome-os-imposed-windows-form-minimum-size-limit
    
        Private Const WM_WINDOWPOSCHANGING As Integer = &H46
        Private Const WM_GETMINMAXINFO As Integer = &H24
        Protected Overrides Sub WndProc(ByRef m As Message)
            If m.Msg = WM_WINDOWPOSCHANGING Then
                Dim windowPos As WindowPos = CType(m.GetLParam(GetType(WindowPos)), WindowPos)
    
                ' Make changes to windowPos
    
                ' Then marshal the changes back to the message
                Marshal.StructureToPtr(windowPos, m.LParam, True)
            End If
    
            MyBase.WndProc(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 Then
                Dim minMaxInfo As MINMAXINFO = DirectCast(m.GetLParam(GetType(MINMAXINFO)), MINMAXINFO)
                minMaxInfo.ptMinTrackSize.X = Me.MinimumSize.Width
                minMaxInfo.ptMinTrackSize.Y = Me.MinimumSize.Height
                Marshal.StructureToPtr(minMaxInfo, m.LParam, True)
            End If
        End Sub
    
        Private Structure WindowPos
            Public hwnd As IntPtr
            Public hwndInsertAfter As IntPtr
            Public x As Integer
            Public y As Integer
            Public width As Integer
            Public height As Integer
            Public flags As UInteger
        End Structure
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure MINMAXINFO
            Dim ptReserved As Point
            Dim ptMaxSize As Point
            Dim ptMaxPosition As Point
            Dim ptMinTrackSize As Point
            Dim ptMaxTrackSize As Point
        End Structure
    
        .... rest of the form
    
    End Class
    
    0 讨论(0)
  • 2020-11-29 09:25

    When playing with minimum form size, I noticed, that minimum form size is restricted to system minimum form size in Form.SetBoundsCore(...). When I look into IL disassemly, I found, that this .Net method always corrects what you give it (width and height) to SystemInformation.MinimumWindowSize if they are smaller and the form don't have a parent and its FormBorderStyle is FixedSingle, Fixed3D, FixedDialog or Sizable.

    The easiest solution to this problem is not handling WM_WINDOWPOSCHANGING, but simply setting FormBorderStyle = System.Windows.Forms.FormBorderStyle.None in the form constructor.

    0 讨论(0)
  • 2020-11-29 09:28

    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.

    0 讨论(0)
  • 2020-11-29 09:33

    I followed Zach's answer and it almost solved my problem. However, in a dual monitor setup, the form disappeared when it was maximized on the second screen. For some reason Windows positioned the form outside the visible region. Adding a test for the primary screen solved this problem for me:

    if (m.Msg == (int)CWinApi.Messages.WM_GETMINMAXINFO)
    {
        if (this.FormBorderStyle == System.Windows.Forms.FormBorderStyle.None)
        {
            Screen screen = Screen.FromControl(this);
    
            if (screen.Primary)
            {
                CWinApi.MINMAXINFO minMaxInfo = (CWinApi.MINMAXINFO)m.GetLParam(typeof(CWinApi.MINMAXINFO));
    
                minMaxInfo.ptMaxSize.x = screen.WorkingArea.Size.Width;
                minMaxInfo.ptMaxSize.y = screen.WorkingArea.Size.Height;
                minMaxInfo.ptMaxPosition.x = screen.WorkingArea.X;
                minMaxInfo.ptMaxPosition.y = screen.WorkingArea.Y;
    
                System.Runtime.InteropServices.Marshal.StructureToPtr(minMaxInfo, m.LParam, true);
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题