DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

フォームのリサイズが終了するまでコントロールの大きさを変えない

Windowsの設定によっては、ユーザーがフォームの境界線をマウスでドラッグしてフォームのサイズを変えている間中絶え間なくフォームのResizeやLayoutイベントが発生し、コントロールの再配置(レイアウト)が行われます。これではパフォーマンスが低下してしまいますので、フォームのリサイズ中は何もしてほしくないというケースもあるでしょう。ここでは、フォームのリサイズ中は何もせず、リサイズが完了してマウスボタンを放したときに初めてコントロールの再配置が行われるようにする方法を紹介します。

Form.ResizeBegin、ResizeEndイベントを使用する方法

ユーザーがフォームのリサイズを開始した時にForm.ResizeBeginイベントが、終了した時にForm.ResizeEndイベントが発生します。これらのイベントは、.NET Framework 2.0以降で使用できます。

なおこれらのイベントは、ユーザーがフォームを移動させた時にも発生します。

以下の例では、フォームのOnResizeメソッドをオーバーライドすることでリサイズ中は何もしないようにして、ResizeEndイベントでコントロールの再配置をしています。このコードは、フォームのクラス内に記述してください。

VB.NET
コードを隠すコードを選択
'リサイズ中は、何もしない
Protected Overrides Sub OnResize(ByVal e As EventArgs)
End Sub

'フォームのResizeEndイベントハンドラ
Private Sub Form1_ResizeEnd(ByVal sender As Object, ByVal e As EventArgs) _
        Handles MyBase.ResizeEnd
    'リサイズ終了後、再描画とレイアウトロジックを実行する
    Me.Invalidate()
    Me.PerformLayout()
End Sub
C#
コードを隠すコードを選択
//リサイズ中は、何もしない
protected override void OnResize(EventArgs e)
{
}

//フォームのResizeEndイベントハンドラ
private void Form1_ResizeEnd(object sender, EventArgs e)
{
    //リサイズ終了後、再描画とレイアウトロジックを実行する
    this.Invalidate();
    this.PerformLayout();
}

または、Control.SuspendLayoutメソッドでレイアウトロジックを中断して、Control.ResumeLayoutメソッドで再開するという方法もあります。この方法の例は、以下のようになります。ただしこの方法では、リサイズ中フォームのResizeイベントは発生します。

VB.NET
コードを隠すコードを選択
[VB.NET]
VB.NETのコードは現在準備中です。
Convert C# to VB.NET により、
下記のC#のコードをVB.NETに変換したものを参考にしてください。
C#
コードを隠すコードを選択
//フォームのResizeBeginイベントハンドラ
private void Form1_ResizeBegin(object sender, EventArgs e)
{
    //レイアウトロジックを中断する
    this.SuspendLayout();
}

//フォームのResizeEndイベントハンドラ
private void Form1_ResizeEnd(object sender, EventArgs e)
{
    //レイアウトロジックを再開する
    this.ResumeLayout();
}

Application.Idleイベントを使用する方法

.NET Framework 1.1以前で、ResizeBeginとResizeEndイベントが使用できない場合は、工夫が必要になります。

まずは、Application.Idleイベントを使用する方法を考えてみます。フォームのサイズを変更している最中はApplication.Idleイベントが発生せず、変更完了してから発生するという特徴を利用します。

この方法によるコードは次のようになります。

VB.NET
コードを隠すコードを選択
'OnResizeのEventArgsを保持する
Dim resizeEA As EventArgs = Nothing

'フォームのサイズが変更した時
Protected Overrides Sub OnResize(ByVal e As EventArgs)
    If resizeEA Is Nothing Then
        resizeEA = e
        AddHandler Application.Idle, AddressOf OnIdle
    End If
End Sub

'アプリケーションがアイドル状態になった時
Private Sub OnIdle(ByVal s As Object, ByVal e As EventArgs)
    If Not (resizeEA Is Nothing) Then
        '基本クラスのOnResizeを呼び出す
        MyBase.OnResize(resizeEA)
        resizeEA = Nothing
        RemoveHandler Application.Idle, AddressOf OnIdle
    End If
End Sub
C#
コードを隠すコードを選択
//OnResizeのEventArgsを保持する
EventArgs resizeEA = null; 

//フォームのサイズが変更した時
protected override void OnResize(EventArgs e)
{
    if (resizeEA == null)
    {
        resizeEA = e;
        Application.Idle += new EventHandler(OnIdle);
    }
}

//アプリケーションがアイドル状態になった時
private void OnIdle(object s, EventArgs e)
{
    if (resizeEA != null)
    {
        //基本クラスのOnResizeを呼び出す
        base.OnResize(resizeEA);
        resizeEA = null;
        Application.Idle -= new EventHandler(OnIdle);
    }
}

WndProcをオーバーライドする方法

フォームのWndProcをオーバーライドし、WM_EXITSIZEMOVEメッセージを待ち、送られてきた時にコントロールを配置するという方法もあります。なおWM_EXITSIZEMOVEメッセージは、ResizeEndイベントと同様に、ユーザーがフォームを移動させた時にも送られてきます。この方法は、ニュースグループの投稿(リンク切れ)を参考にさせていただきました。

VB.NET
コードを隠すコードを選択
'Imports System.Security.Permissions

'OnResizeをオーバーライドし、何もしない
Protected Overrides Sub OnResize(ByVal e As EventArgs)
End Sub

Private Const WM_EXITSIZEMOVE As Integer = &H232

<SecurityPermission(SecurityAction.Demand, _
    Flags:=SecurityPermissionFlag.UnmanagedCode)> _
Protected Overrides Sub WndProc(ByRef m As Message)
    If m.Msg = WM_EXITSIZEMOVE Then
        'リサイズ終了後、再描画とレイアウトロジックを実行する
        Me.Invalidate()
        Me.PerformLayout()
    End If
    MyBase.WndProc(m)
End Sub
C#
コードを隠すコードを選択
//using System.Security.Permissions;

//OnResizeをオーバーライドし、何もしない
protected override void OnResize(EventArgs e)
{
}

private const int WM_EXITSIZEMOVE = 0x232;

[SecurityPermission(SecurityAction.Demand,
    Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) 
{
    if (m.Msg == WM_EXITSIZEMOVE) 
    {
        //リサイズ終了後、再描画とレイアウトロジックを実行する
        this.Invalidate();
        this.PerformLayout();
    }
    base.WndProc(ref m);
}
  • 履歴:
  • 2007/1/15 「.NET Framework 2.0以降で、Form.ResizeBegin、ResizeEndイベントを使用する方法」を追加。
  • 2010/6/30 WndProcにSecurityPermissionAttributeを付けた。
  • 2013/12/9 SecurityAction.LinkDemandの代わりにSecurityAction.Demandを使うようにした。
  • 2014/9/24 冒頭部分の書き換え。「Form.ResizeBegin、ResizeEndイベントを使用する方法」を一番上に。SuspendLayoutとResumeLayoutを使用する方法を追加。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。