Overcome OS Imposed Windows Form Minimum Size Limit
在我正在开发的应用程序中,我需要使窗口形状小于操作系统所强加的最小高度限制(在Vista中为36 px)。我尝试拦截WM_GETMINMAXINFO并提供我自己的信息来覆盖操作系统限制,但这仅对用户有效。从代码中,我可以将高度设置为小于限制的值,但是我的更改仅在WM_WINDOWPOSCHANGED发布到消息队列(更改高度后立即发生)之前有效。
经过反复试验和反复试验,我找到了解决方案。我正在重写OnResize并使表单的大小与其中的ListBox保持一致(请参阅我对John Saunders答案的评论)。
正如我在问题中提到的,我注意到在发送WM_WINDOWPOSCHANGED之后,表单的大小会变小。进一步的调查表明,大小回归实际上是在发送WM_WINDOWPOSCHANGING时开始的。
WM_WINDOWPOSCHANGING是WM_WINDOWPOSCHANGED的姐妹消息,它在窗口大小实际更改之前发生。我不知道为什么,但是由于某种原因,WM_WINDOWPOSCHANGING盲目地将窗体的大小与操作系统指定的限制相一致(显然,它不使用WM_GETMINMAXINFO查询窗口)。因此,我需要拦截WM_WINDOWPOSCHANGING并用我真正想要的大小覆盖它。
这意味着我不再使用OnResize来符合表单的大小,而是在收到WM_WINDOWPOSCHANGING时符合了表单的大小。这甚至比OnResize更好,因为在更改大小时不会出现关联的闪烁,而在OnResize期间符合大小时,该闪烁不会再次发生。
此外,有必要拦截并覆盖WM_GETMINMAXINFO,否则,即使拦截WM_WINDOWPOSCHANGING也对您没有好处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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; } |
阿列克谢太近了!
1 2 3 4 | 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); } |
帮我把戏。我将窗体的最小大小设置为我想要窗体的实际大小。
在我的项目中,要做的只是使Form变小,这可能是因为设置Minimum size会触发SetBoundsCore或可能我正在做其他事情来触发它;在这种情况下,我想您必须以某种方式自己触发SetBoundsCore。
当我使用最小表单尺寸播放时,我注意到,最小表单尺寸在Form.SetBoundsCore(...)中被限制为系统最小表单尺寸。当我沮丧地研究IL时,我发现此.Net方法始终会纠正您给SystemInformation.MinimumWindowSize赋予的内容(宽度和高度)(如果它们较小并且表单没有父级,并且其FormBorderStyle为FixedSingle), Fixed3D,FixedDialog或Sizable。
此问题的最简单解决方案是不处理WM_WINDOWPOSCHANGING,而只是在表单构造函数中设置FormBorderStyle = System.Windows.Forms.FormBorderStyle.None。
我希望为此可以给Zach多于1个,这很棒,并节省了我的培根。
对于将来的读者,这是Zach代码的VB翻译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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 |
我听了扎克的回答,这几乎解决了我的问题。但是,在双监视器设置中,当窗体在第二个屏幕上最大化时,窗体消失了。由于某种原因,Windows将表单放置在可见区域之外。为主屏幕添加测试可以为我解决此问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); } } } |
您的意思是,除了使用其他操作系统之外?
"不要使用表格"怎么样?您需要显示多少东西?一个像素?是否需要完整的Windows窗体功能?
现在,我不完全知道如何执行上述操作,但这可能对您来说是一个开始-请在(边界)框外思考。