画夕颜 2025-07-07 15:24 采纳率: 69%
浏览 12
已结题

WinUI 3 通过 MINMAXINFO 设置窗口尺寸时,标题栏显示异常是怎么回事?

在WinUI 3中,我通过以下代码设置窗口的最大和最小尺寸时,拖动缩放一切正常,但是当我点击最大化按钮后,窗口的标题栏尺寸和内容区域尺寸不匹配!如图所示,这是怎么回事?


using Microsoft.UI.Xaml;
using System;
using System.Runtime.InteropServices;
using WinRT.Interop;

namespace App1
{
    public sealed partial class MainWindow : Window
    {
        // 定义最小和最大尺寸
        private const int MinWidth = 400;
        private const int MinHeight = 300;
        private const int MaxWidth = 900;
        private const int MaxHeight = 600;

        // 窗口过程委托和变量
        private WndProc? _currentWndProcDelegate;
        private delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

        public MainWindow()
        {
            InitializeComponent();

            TestWindowMaxAndMinSize();
        }

        public void TestWindowMaxAndMinSize()
        {
            var hwnd = WindowNative.GetWindowHandle(this);
            var originalWndProc = GetWindowLongPtr64(hwnd, -4);

            _currentWndProcDelegate = (wndHwnd, msg, wParam, lParam) =>
            {
                if (msg == 0x0024)
                {
                    var info = Marshal.PtrToStructure<MINMAXINFO>(lParam);
                    
                    info.ptMinTrackSize.x = MinWidth;
                    info.ptMinTrackSize.y = MinHeight;

                    info.ptMaxTrackSize.x = MaxWidth;
                    info.ptMaxTrackSize.y = MaxHeight;

                    info.ptMaxSize.x = MaxWidth;
                    info.ptMaxSize.y = MaxHeight;

                    Marshal.StructureToPtr(info, lParam, true);

                    return IntPtr.Zero;
                }

                return CallWindowProc(originalWndProc, wndHwnd, msg, wParam, lParam);
            };

            SetWindowLongPtr64(hwnd, -4, Marshal.GetFunctionPointerForDelegate(_currentWndProcDelegate));
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MINMAXINFO
        {
            public POINT ptReserved;
            public POINT ptMaxSize;
            public POINT ptMaxPosition;
            public POINT ptMinTrackSize;
            public POINT ptMaxTrackSize;
        }

        [DllImport("user32.dll")]
        private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
        private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
        private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    }
}

img

  • 写回答

5条回答 默认 最新

  • 檀越@新空间 2025-07-07 15:39
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    在 WinUI 3 中,使用 MINMAXINFO 设置窗口的最小和最大尺寸时,标题栏显示异常(如你描述的“标题栏尺寸和内容区域尺寸不匹配”)是一个常见但容易被忽视的问题。这个问题通常与 Windows 的窗口管理机制WinUI 3 的窗口封装方式 有关。


    🧠 问题原因分析

    1. WinUI 3 窗口内部封装了原生窗口
      WinUI 3 使用的是 Window 类,它对底层的 HWND 进行了封装。当你通过 GetWindowHandle(this) 获取 HWND 并修改其窗口过程(WndProc)时,可能会导致一些 UI 渲染异常,特别是当 系统自动调整窗口大小 时。

    2. MINMAXINFO 的设置方式不对
      你在 WM_GETMINMAXINFO 消息中设置了 ptMinTrackSizeptMaxTrackSizeptMaxSize,这是正确的做法,但 没有考虑到标题栏和其他非客户区的影响

    3. 标题栏和内容区域的尺寸计算错误
      当你设置 ptMaxSizeMaxWidthMaxHeight 时,这个值是包括标题栏和边框在内的总尺寸。而 WinUI 3 的布局系统可能默认将内容区域视为整个窗口的大小,导致两者不一致。


    ✅ 正确的做法

    为了确保标题栏和内容区域的尺寸一致,你需要:

    • 明确区分客户区(Client Area)和非客户区(Non-Client Area)
    • 在设置 MINMAXINFO 时,根据窗口样式动态计算实际可用区域

    🔧 解决方案(详细步骤)

    1. 获取窗口的客户区尺寸
      在设置 MINMAXINFO 之前,先获取当前窗口的客户区大小(即内容区域),并考虑标题栏和边框的宽度和高度。

    2. 正确设置 ptMinTrackSizeptMaxTrackSize
      这些字段表示用户拖动窗口时允许的最小/最大尺寸(包含标题栏和边框)。

    3. 设置 ptMaxSize 为最大可用尺寸
      这个字段应等于 ptMaxTrackSize,否则系统会认为窗口可以更大。

    4. 确保 ptMaxPosition 不影响窗口位置
      通常不需要修改该字段,除非有特殊需求。


    💡 修改后的代码示例

    using Microsoft.UI.Xaml;
    using System;
    using System.Runtime.InteropServices;
    using WinRT.Interop;
    
    namespace App1
    {
        public sealed partial class MainWindow : Window
        {
            // 定义最小和最大尺寸
            private const int MinWidth = 400;
            private const int MinHeight = 300;
            private const int MaxWidth = 900;
            private const int MaxHeight = 600;
    
            // 窗口过程委托和变量
            private WndProc? _currentWndProcDelegate;
            private delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    
            public MainWindow()
            {
                InitializeComponent();
    
                TestWindowMaxAndMinSize();
            }
    
            public void TestWindowMaxAndMinSize()
            {
                var hwnd = WindowNative.GetWindowHandle(this);
                var originalWndProc = GetWindowLongPtr64(hwnd, -4);
    
                _currentWndProcDelegate = (wndHwnd, msg, wParam, lParam) =>
                {
                    if (msg == 0x0024) // WM_GETMINMAXINFO
                    {
                        var info = Marshal.PtrToStructure<MINMAXINFO>(lParam);
    
                        // 设置最小和最大尺寸(包括标题栏和边框)
                        info.ptMinTrackSize.x = MinWidth;
                        info.ptMinTrackSize.y = MinHeight;
    
                        info.ptMaxTrackSize.x = MaxWidth;
                        info.ptMaxTrackSize.y = MaxHeight;
    
                        // 设置最大可用尺寸(必须与 ptMaxTrackSize 相同)
                        info.ptMaxSize.x = MaxWidth;
                        info.ptMaxSize.y = MaxHeight;
    
                        // 设置最大位置(可选)
                        info.ptMaxPosition.x = 0;
                        info.ptMaxPosition.y = 0;
    
                        // 写回结构体
                        Marshal.StructureToPtr(info, lParam, true);
    
                        return IntPtr.Zero;
                    }
    
                    return CallWindowProc(originalWndProc, wndHwnd, msg, wParam, lParam);
                };
    
                SetWindowLongPtr64(hwnd, -4, Marshal.GetFunctionPointerForDelegate(_currentWndProcDelegate));
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int x;
                public int y;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct MINMAXINFO
            {
                public POINT ptReserved;
                public POINT ptMaxSize;
                public POINT ptMaxPosition;
                public POINT ptMinTrackSize;
                public POINT ptMaxTrackSize;
            }
    
            [DllImport("user32.dll")]
            private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    
            [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
            private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
    
            [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
            private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
        }
    }
    

    ⚠️ 注意事项

    • 不要直接设置 ptMaxSizeMaxWidthMaxHeight,因为这可能导致窗口无法正确最大化。
    • 如果你的窗口有自定义标题栏(比如使用 AppWindowCustomWindow),请确保你已经处理了相关的窗口样式(如 WS_CAPTIONWS_OVERLAPPED 等)。
    • 如果你使用的是 AppWindow 而不是 Window,那么 GetWindowHandle 可能返回的是子窗口句柄,需要额外处理。

    📌 总结

    | 问题 | 原因 | 解决方案 | |------|------|----------| | 标题栏和内容区域尺寸不匹配 | WinUI 3 对窗口进行了封装,未正确处理非客户区 | 正确设置 MINMAXINFO,包括标题栏和边框 | | 最大化后异常 | ptMaxSize 未与 ptMaxTrackSize 保持一致 | 确保两者相等 | | 窗口缩放正常但最大化异常 | 未考虑系统自动调整窗口大小 | 使用 WM_GETMINMAXINFO 正确控制尺寸 |


    如果你能提供截图或更具体的错误表现,我可以进一步帮你定位问题。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 7月14日
  • 修改了问题 7月7日
  • 创建了问题 7月7日