Q098. 最大化ボタンを有効にしながら、ユーザーによるサイズ変更を不可にするには?

A.WPF の Window は ResizeMode プロパティを NoResize にすると最大化ボタン等が非表示になります。Window.OnStateCnanged メソッドをオーバーライドするか、HwndSource クラスにメッセージを処理するウィンドウプロシージャを登録します。

本記事は、以下 MSDN フォーラムスレッドの備忘録です。ご了承ください。

関連記事:
Window の手動サイズ変更だけを不可にしたい
Window StateChanging event in WPF



Window.OnStateChanged をオーバーライド

Window.OnStateChanged メソッドをオーバーライドします。OnStateChanged 内で WindowState を設定しているため、OnStateChanged が再帰的に呼び出されますが、この手法を使わないとノーマル状態から最大化する際、画面の最大化ができません。ちょっとややこしいですね。

// C#
bool _isDuringMaximizing;

protected override void OnStateChanged(EventArgs e)
{
    if (WindowState == WindowState.Maximized)
    {
        MinWidth = 0;
        MinHeight = 0;
        MaxWidth = int.MaxValue;
        MaxHeight = int.MaxValue;

        if (!_isDuringMaximizing)
        {
            _isDuringMaximizing = true;
            WindowState = WindowState.Normal;
            WindowState = WindowState.Maximized;
            _isDuringMaximizing = false;
        }
    }
    else if (!_isDuringMaximizing)
    {
        MinWidth = 1024;
        MinHeight = 768;
        MaxWidth = 1024;
        MaxHeight = 768;
    }
    base.OnStateChanged(e);
}
' VB.NET
Private _isDuringMaximizing As Boolean

Protected Overrides Sub OnStateChanged(e As EventArgs)
    If WindowState = WindowState.Maximized Then
        MinWidth = 0
        MinHeight = 0
        MaxWidth = Int32.MaxValue
        MaxHeight = Int32.MaxValue

        If Not _isDuringMaximizing Then
            _isDuringMaximizing = True
            WindowState = WindowState.Normal
            WindowState = WindowState.Maximized
            _isDuringMaximizing = False
        End If

    ElseIf Not _isDuringMaximizing Then
        MinWidth = 1024
        MinHeight = 768
        MaxWidth = 1024
        MaxHeight = 768
    End If
    MyBase.OnStateChanged(e)
End Sub

ウィンドウプロシージャ版

メッセージに応じて MaxHeight・MinHeight・MaxWidth・MinWidth を設定するウィンドウプロシージャを作成し、HwndSource.AddHook メソッドにフックとして登録します。

// C#
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace WpfApplication1 {
    public partial class MainWindow : Window {
        private static readonly int SYSCOMMAND  = 0x0112;
        private static readonly int SC_MINIMIZE = 0xf020;
        private static readonly int SC_MAXIMIZE = 0xF030;
        private static readonly int SC_RESTORE  = 0xf120;
        
        public MainWindow() {
            InitializeComponent();
            this.SourceInitialized += Window1_SourceInitialized;
        }

        void Window1_SourceInitialized(object sender, EventArgs e) {
            HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
            source.AddHook(new HwndSourceHook(WndProc));
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            // 元のサイズに戻す
            if (msg == SYSCOMMAND && SC_RESTORE == wParam.ToInt32()) {
                this.MaxHeight = this.MinHeight = 350;
                this.MaxWidth = this.MinWidth = 525;
            }

            // 最小化
            if (msg == SYSCOMMAND && SC_MINIMIZE == wParam.ToInt32()) {
                this.MaxHeight = this.MaxWidth = int.MaxValue;
                this.MinHeight = this.MinWidth = 0.0;
            }

            // 最大化
            if (msg == SYSCOMMAND && SC_MAXIMIZE == wParam.ToInt32()) {
                this.MaxHeight = this.MaxWidth = int.MaxValue;
                this.MinHeight = this.MinWidth = 0.0;
            }
            handled = false;
            return IntPtr.Zero;
        }
    }
}
' VB.NET
Imports System.Runtime.InteropServices
Imports System.Windows
Imports System.Windows.Interop

Class MainWindow
    Private Shared ReadOnly SYSCOMMAND As Int32 = &H112
    Private Shared ReadOnly SC_MINIMIZE As Int32 = &HF020
    Private Shared ReadOnly SC_MAXIMIZE As Int32 = &HF030
    Private Shared ReadOnly SC_RESTORE As Int32 = &HF120

    Public Sub New()
        InitializeComponent()
        AddHandler Me.SourceInitialized, AddressOf Window1_SourceInitialized
    End Sub

    Private Sub Window1_SourceInitialized(sender As Object, e As EventArgs)
        Dim source = HwndSource.FromHwnd(New WindowInteropHelper(Me).Handle)
        source.AddHook(New HwndSourceHook(AddressOf WndProc))
    End Sub

    Private Function WndProc(hwnd As IntPtr, msg As Int32,
        wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr

        ' 元のサイズに戻す
        If (msg = SYSCOMMAND And SC_RESTORE = wParam.ToInt32()) Then
            Me.MaxHeight = 350 : Me.MinHeight = 350
            Me.MaxWidth = 525 : Me.MinWidth = 525
        End If

        ' 最小化
        If (msg = SYSCOMMAND And SC_MINIMIZE = wParam.ToInt32()) Then
            Me.MaxHeight = Int32.MaxValue : Me.MaxWidth = Int32.MaxValue
            Me.MinHeight = 0.0 : Me.MinWidth = 0.0
        End If

        ' 最大化
        If (msg = SYSCOMMAND And SC_MAXIMIZE = wParam.ToInt32()) Then
            Me.MaxHeight = Int32.MaxValue : Me.MaxWidth = Int32.MaxValue
            Me.MinHeight = 0.0 : Me.MinWidth = 0.0
        End If

        handled = False
        Return IntPtr.Zero
    End Function
End Class

WPF FAQ の目次に戻る