WinUI3で最小ウインドウサイズを指定する

もくじ
https://tera1707.com/entry/2022/02/06/144447#WinUI3

やりたいこと

WPFとかでよくやっていた、ウインドウサイズをこれ以上小さくできないようにしたい、ということをWinUI3でもやりたい。

が、ぱっと調べただけでは、WinUI3の標準のやり方でそういったことはできないっぽい。
でも何とかしたい。

前提

  • VisualStudio2022 Community 17.3.0
  • WinUI3 と パッケージプロジェクト
  • Windows App SDK 1.1.3
  • PInvoke.User32 v0.7.124のnugetパッケージを使用

やりかた

MSのgithubに、同じことで困っている人のissueが上がっていた。

github.com

これを、まるっとまねさせてもらう形で、ちょっとだけ改造して実現する。

サンプルコード

ざっくり処理の流れとしては

  • WinUI3ウインドウのウインドウプロシージャで、
  • WM_GETMINMAXINFOを処理し、その中で最小のウインドウサイズを指定する

となる。

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

// nuget PInvoke.User32 0.7.124

namespace MinWidthJikken
{
    public sealed partial class MainWindow : Window
    {
        // 最小の大きさ
        int MinWidth = 400;
        int MinHeight = 300;

        public MainWindow()
        {
            this.InitializeComponent();
            SubClassing();
        }

        private NativeMethods.WinProc newWndProc = null;
        private IntPtr oldWndProc = IntPtr.Zero;

        private void SubClassing()
        {
            // ウインドウのハンドルを取ってくる
            var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
            newWndProc = new NativeMethods.WinProc(NewWindowProc);
            oldWndProc = NativeMethods.SetWindowLong(hwnd, PInvoke.User32.WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
        }

        private IntPtr NewWindowProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
        {
            switch (Msg)
            {
                case PInvoke.User32.WindowMessage.WM_GETMINMAXINFO:
                    // ウインドウのサイズが変わるたびにここを通る
                    var dpi = PInvoke.User32.GetDpiForWindow(hWnd);
                    float scalingFactor = (float)dpi / 96;

                    // ここで、最上の大きさをMINMAXINFOに入れて指定する
                    NativeMethods.MINMAXINFO minMaxInfo = Marshal.PtrToStructure<NativeMethods.MINMAXINFO>(lParam);
                    minMaxInfo.ptMinTrackSize.x = (int)(MinWidth * scalingFactor);
                    minMaxInfo.ptMinTrackSize.y = (int)(MinHeight * scalingFactor);
                    Marshal.StructureToPtr(minMaxInfo, lParam, true);
                    break;

            }
            // WM_GETMINMAXINFO以外の、自分で処理したくないMsgは、もとのWndProcに任せる
            return NativeMethods.CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
        }
    }

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

        public delegate IntPtr WinProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32")]
        public static extern IntPtr SetWindowLong(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc);
        [DllImport("user32.dll")]
        public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam);
    }
}

結果、

(見た目ではわからないが)これ以上小さくできないウインドウになった。

参考

MSのgithubのissue
こういうのが標準でできないのは、WinUI3がUWPの血も受け継いでいるからなんでしょうか。

https://github.com/microsoft/microsoft-ui-xaml/issues/2945

サブクラス化とは?のわかりやすい日本語解説
これがあれば、なんかいろんなことができそう。。

http://wisdom.sakura.ne.jp/system/winapi/win32/win64.html