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;
}
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
8条答案
按热度按时间y53ybaqx1#
经过大量的实验和反复试验,我发现了一个解决方案。我重写了OnResize,并使表单的大小符合其中的ListBox(请参阅我对John Saunders回答的评论)。
正如我在我的问题中提到的,我注意到在发送WM_WINDOWPOSCHANGED后,窗体的大小会退化。进一步的调查显示,大小退化实际上是在发送WM_WINDOWPOSCHANGING时开始的。
WM_WINDOWPOSCHANGING是WM_WINDOWPOSCHANGED的姐妹消息,它在窗口大小实际改变之前发生。我不知道为什么,但是由于某种原因,WM_WINDOWPOSCHANGING盲目地使窗体的大小符合OS指定的限制(显然它没有用WM_GETMINMAXINFO查询窗口)。因此,我需要拦截WM_WINDOWPOSCHANGING并用我真正想要的大小覆盖它。
这意味着我不再使用OnResize来确定窗体的大小,而是在接收WM_WINDOWPOSCHANGING时确定窗体的大小。这甚至比OnResize更好,因为在OnResize期间,当更改大小时不会出现相关的 Flink ,然后在更改大小时再次更改。
另外,有必要拦截并覆盖WM_GETMINMAXINFO,否则,即使拦截WM_WINDOWPOSCHANGING也没有任何好处。
yuvru6vn2#
阿列克谢是如此接近!
我把窗体的最小大小设置为我想要的窗体的实际大小。
在我的项目中,这就是我所要做的,以使窗体变小,这可能是因为设置最小大小触发了SetBoundsCore,也可能是我正在做的其他事情触发了它;在这种情况下,我猜你必须 * 以某种方式 * 自己触发SetBoundsCore。
vzgqcmou3#
当使用最小窗体大小时,我注意到,最小窗体大小被限制为Form.SetBoundsCore(...)中的系统最小窗体大小。当我查看IL反汇编时,我发现,如果SystemInformation.MinimumWindowSize更小,并且窗体没有父窗体,并且其FormBorderStyle是FixedSingle,Fixed3D,FixedDialog或Sizable,则此.Net方法总是更正您给予的内容(宽度和高度)。
解决这个问题最简单的方法不是处理WM_WINDOWPOSCHANGING,而是在表单构造函数中设置FormBorderStyle = System.Windows.Forms.FormBorderStyle.None。
u7up0aaq4#
我希望我能给予Zach更多的+1,它很棒,救了我的培根。对于未来的读者,这里是Zach代码的VB翻译:
mefy6pfw5#
你的意思是,除了使用不同的操作系统?
“不使用窗体”怎么样?你需要显示多大的东西?一个像素?它需要完整的Windows窗体功能吗?
现在,我不知道如何做到这一点,但它可能是一个开始,为您-认为以外的(边界)框。
nue99wik6#
我按照Zach的回答,它几乎解决了我的问题。然而,在双显示器设置中,当窗体在第二个屏幕上最大化时,窗体消失了。由于某种原因,Windows将窗体定位在可见区域之外。为主屏幕添加测试为我解决了这个问题:
jjhzyzn07#
有没有人有这个的WPF版本?我不能让它在我的窗口上工作,似乎没有办法调用
在向WndProc函数添加钩子时,请使用此函数。
7tofc5zh8#
我正在使用C# / WinForms,今天我遇到了这个限制。
经过一些测试,我发现设置
MinimumSize = new Size(1, 1);
将使用户能够缩小窗口超出系统限制,所以没有必要处理WM_GETMINMAXINFO
消息-事实上,设置MinimumSize
后,WM_GETMINMAXINFO
将返回ptMinTrackSize
中的设置值,只是默认情况下MinimumSize
设置为(0, 0)
,这会导致返回系统默认值。尽管将
MinimumSize
设置为(1, 1)
允许用户进一步缩小窗口(即使将FormBorderStyle
设置为Sizable (default)
),但当我尝试在代码中设置大小时,它仍然受到系统默认值的限制。我引用Alexey的回答:当我查看IL反汇编时,我发现,这个.Net方法总是纠正你给予它的东西(宽度和高度)到SystemInformation.MinimumWindowSize,如果它们更小,并且窗体没有父窗体,它的FormBorderStyle是FixedSingle,Fixed 3D,FixedDialog或Sizable。
所以......似乎试图删除这个不必要的约束将是非常困难的。然而,我的程序已经有一个处理
WM_WINDOWPOSCHANGING
的例程,所以我想出了一个简单的解决方案:***(重要)**首先设置
MinimumSize = new Size(1, 1);
,使WM_GETMINMAXINFO
返回(1,1)
,而不是系统默认值,表单只需要设置一次。WM_WINDOWPOSCHANGING
消息时,将大小设置为所需的大小。这个“幻数大小”不应该太大或太小,我使用的是SystemInformation.MinimumWindowSize
。