C#开发WinUI 3如何通过WM_GETMINMAXINFO设置窗口大小限制
实现位置选择
相关窗口尺寸约束逻辑必须写在需要控制尺寸的对应窗口类(如默认项目模板的MainWindow)中,不要写在App类里:
WM_GETMINMAXINFO是系统发送给单个窗口句柄的私有消息,和具体窗口实例强绑定App类仅负责全局应用生命周期管理,不直接持有目标窗口的句柄引用,写在此处需要额外做窗口枚举、句柄匹配,徒增不必要的复杂度- 多窗口场景下,逻辑写在各自窗口类中可以为不同窗口单独配置不同的尺寸限制,互不干扰
具体实现方案
根据你使用的Windows App SDK版本,可以选两种实现方式:
方案1:Windows App SDK 1.3+ 原生API(推荐)
1.3及以上版本框架已经封装了尺寸限制能力,不需要手动处理Win32消息,直接调用内置API即可:
using Microsoft.UI.Windowing; // 在MainWindow构造函数中InitializeComponent()后添加 public MainWindow() { InitializeComponent(); var appWindow = this.AppWindow; // 设置最小尺寸,单位为物理像素 appWindow.ResizeManager.MinSize = new Windows.Graphics.SizeInt32(800, 600); // 设置最大尺寸,不需要限制可删除此行 appWindow.ResizeManager.MaxSize = new Windows.Graphics.SizeInt32(1920, 1080); }
该方案底层已经自动处理了WM_GETMINMAXINFO消息的拦截和参数填充,稳定性更高,代码量最少。
方案2:手动处理WM_GETMINMAXINFO消息(兼容所有版本)
如果使用的是1.3以下版本的Windows App SDK,可以通过窗口子类化拦截消息实现:
- 首先在窗口类中定义所需的Win32互操作结构、常量和函数导入:
using System.Runtime.InteropServices; private const int WM_GETMINMAXINFO = 0x0024; [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X; public int Y; public POINT(int x, int y) { X = x; Y = y; } } [StructLayout(LayoutKind.Sequential)] private struct MINMAXINFO { public POINT ptReserved; public POINT ptMaxSize; public POINT ptMaxPosition; public POINT ptMinTrackSize; public POINT ptMaxTrackSize; } private delegate nint WndProc(nint hWnd, uint msg, nint wParam, nint lParam, nint uIdSubclass, nint dwRefData); private WndProc _wndProc; // 必须定义为实例字段,防止被GC回收 [DllImport("Comctl32.dll", CharSet = CharSet.Auto)] private static extern bool SetWindowSubclass(nint hWnd, WndProc pfnSubclass, nint uIdSubclass, nint dwRefData); [DllImport("Comctl32.dll", CharSet = CharSet.Auto)] private static extern nint DefSubclassProc(nint hWnd, uint msg, nint wParam, nint lParam);
- 在MainWindow构造函数中安装窗口子类钩子:
public MainWindow() { InitializeComponent(); _wndProc = CustomWindowProc; nint hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); SetWindowSubclass(hwnd, _wndProc, nint.Zero, nint.Zero); }
- 实现自定义窗口过程,拦截
WM_GETMINMAXINFO修改尺寸限制:
private nint CustomWindowProc(nint hWnd, uint msg, nint wParam, nint lParam, nint uIdSubclass, nint dwRefData) { if (msg == WM_GETMINMAXINFO) { MINMAXINFO mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam); // 设置最小拖拽尺寸 mmi.ptMinTrackSize = new POINT(800, 600); // 设置最大拖拽尺寸,不需要限制可删除此行 mmi.ptMaxTrackSize = new POINT(1920, 1080); Marshal.StructureToPtr(mmi, lParam, false); return 0; } return DefSubclassProc(hWnd, msg, wParam, lParam); }
注意事项
- 上述所有API中使用的尺寸单位均为物理像素,不是WinUI XAML中默认使用的有效像素(EPX)。如果需要和XAML设计尺寸严格对齐,需要根据当前显示器DPI做单位换算:
物理像素 = 有效像素 * (系统DPI / 96) - 不要尝试在
App类中安装全局窗口钩子拦截消息,会增加全局消息处理开销,还容易引发多窗口场景下的逻辑冲突 - 存储窗口过程委托的
_wndProc字段不能写成方法内的局部变量,否则会被GC回收,触发访问违例崩溃
内容的提问来源于stack exchange,提问作者user19414975
相关产品推荐
相关产品推荐

