🖥️

続・WindowStyle を None にしてカスタム ウィンドウを作ってみる

に公開

前回の記事の続きです。

https://zenn.dev/karamem0/articles/2013_05_27_000000

前回は Win32 API を利用してカスタム ウィンドウの動作を制御しました。あとから気付きましたが、前回のままでは最小化または最大化したときにアニメーションが実行されないという問題が発生します。文字だけでは伝わりにくいですが、ウィンドウがパッと表示・非表示になる印象です。細かい点ですが、気になる動作です。

いろいろと検証した結果、ウィンドウ スタイルに WS_CAPTION を設定することで解決できることが分かりました。さっそく前回のコードを修正します。

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    var handle = new WindowInteropHelper(this).Handle;
    if (handle == IntPtr.Zero)
    {
        return;
    }
    // ここから追加
    var style = Win32.GetWindowLong(handle, (int)Win32.GetWindowLongIndex.GWL_STYLE);
    style |= (int)Win32.WindowStyles.WS_CAPTION;
    Win32.SetWindowLong(handle, (int)Win32.GetWindowLongIndex.GWL_STYLE, style);
    // ここまで追加
    HwndSource.FromHwnd(handle).AddHook(this.WindowProc);
}

この修正で問題が解決したかと思いましたが、今度は最大化したときのウィンドウ サイズが正しくなりません。タスク バーの領域は考慮されていますが、上下左右に 8 ピクセルの余白が発生します。どうやら WM_GETMINMAXINFO で計算したロジックが無視されているようで、さまざまな方法を試しましたが、うまくいきませんでした。最終的には WM_GETMINMAXINFO での制御を諦め、最大化時にはウィンドウ内でマージンを設定する方法に変更しました。最大化されたかどうかは WM_SIZE で判定します。

private IntPtr WindowProc(IntPtr handle, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == (int)Win32.WindowMessages.WM_SIZE)
    {
        if ((Win32.ResizeMode)wParam == Win32.ResizeMode.SIZE_MAXIMIZED)
        {
            this.LayoutRoot.Margin = new Thickness(8);
        }
        else
        {
            this.LayoutRoot.Margin = new Thickness(0);
        }
    }
    return IntPtr.Zero;
}

この方法はあまり推奨できませんが、現状では仕方ありません。いずれにしても、これでアニメーションを有効にできました。

Discussion