LoginSignup
3
3

More than 1 year has passed since last update.

[WPF]Windows 11 スナップレイアウトに対応する(要修正)

Last updated at Posted at 2021-10-13

こんにちは。

Windows 11がリリースされて一週間経ちましたが、ウィンドウまわりにスナップレイアウトという機能が実装されましたね。
最大化ボタンまたは元に戻すボタンをホバーすると、下記のようなものが出てきます。
2021-10-13.png
で、任意の領域をクリックすると、その領域にウィンドウがジャストフィットするようにレイアウトされます。

この機能、私が開発しているboiler's Graphicsでも使えるのかなー。そう思っておもむろに起動してみましたが、使えませんでした。

Windows 11 上でデスクトップ アプリのスナップ レイアウトをサポート
https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/apply-snap-layout-menu

上記記事によると、WPFアプリには何やら特別な処理が必要だそうです。

というわけで、当記事ではboiler's GraphicsをWindows 11のスナップレイアウトに対応させてみたいと思います。

で、早速ですが以下が変更したコードです。

MainWindow.xaml.cs
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

namespace boilersGraphics.Views
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Loaded += (o, e) =>
            {
                var source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
                source.AddHook(new HwndSourceHook(WndProc));
            };
        }

        const int WM_NCHITTEST = 0x0084;
        const int HTMAXBUTTON = 9;

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_NCHITTEST)
            {
                int x = lParam.ToInt32() & 0xffff;
                int y = lParam.ToInt32() >> 16;
                var maximizeButton = this.MainWindowInstance.Template.FindName("MaximizeButton", this.MainWindowInstance) as Button;
                var rect = new Rect(maximizeButton.PointToScreen(new Point()), new Size(maximizeButton.Width, maximizeButton.Height));
                if (rect.Contains(new Point(x, y)))
                {
                    handled = true;
                }
                return new IntPtr(HTMAXBUTTON);
            }
            return DefWindowProc(hwnd, msg, wParam, lParam);
        }

        [DllImport("user32.dll")]
        static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
        :
    }
}

要点は以下の通りです。

  1. Loadedイベントでグローバルフックを使う
  2. グローバルフック内でWM_NCHITTESTメッセージを捕捉したらHTMAXBUTTONを返すようにする
  3. 2はマウスポインタが最大化ボタンの上に存在する(ホバーしている)時のみ、handled = trueにする

上記の記事内(英語)には、"For Win32 apps, make sure you are responding appropriately to WM_NCHITTEST (with a return value of HTMAXBUTTON for the maximize/restore button)."と書いてあります。つまりWM_NCHITTESTメッセージが来た時ははHTMAXBUTTONを返せということです。

これで、boiler's Graphicsはどのような挙動をするか確認しました。

Windows11_SnapLayout.gif

おおースナップレイアウトはちゃんと動きます。
しかし、最大化ボタンをクリックしても反応しなくなってしまいました。最小化ボタンと閉じるボタンは正しく反応するのですが。どうしたら良いんだろう...。

(下記2021/10/13 14:17追記)
ソースコードを載せ忘れました。
ここに置いときます。

boiler's Graphics
https://github.com/dhq-boiler/boiler-s-Graphics
ブランチ:feature/windows11

3
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3