Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What are the problem?

posted at

updated at

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

こんにちは。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
3
Help us understand the problem. What are the problem?