ナビゲーション リンクのスキップホーム > ライブラリ > ボタン > ボタンの 2 度押しを防止する
ナビゲーション リンクのスキップ
ホーム の縮小ホーム
ライブラリ の縮小ライブラリ
コントロール の縮小コントロール
コントロールの背景を描画する
コントロールの背景にグラデーションを描画する
コントロールの背景を透過・透明にする
コントロールの表面を描画をする
コントロールに画像の角を丸く描画する
コントロールの角を丸くする
コントロールの境界線を描画をする
コントロールの境界線の角を丸く描画する
コントロールの非クライアント領域を描画する
コントロールを透明にする
コントロールにフォーカスが設定されないようにする
コントロールのショートカットキーを編集・処理する
コントロールの描画を一時停止する
コントロールをマウスで移動する
コンボボックス の縮小コンボボックス
コンボボックスに透かし文字を表示する
コンボボックスに複数項目・複数列表示する
コンボボックスのドロップダウンを画面内に表示する
コンボボックスを独自に描画する
テキストボックス の縮小テキストボックス
テキストボックスに透かし文字を表示する (簡単)
テキストボックスに透かし文字を表示する (実装)
テキストボックスの入力制限をする
フォーム の縮小フォーム
アプリケーション デスクトップ ツール バーを表示する
フォームのファイル メニュー項目を削除する
フォームの最大化、最小化、元のサイズに戻すをキャンセルする
フォームの閉じるボタンを無効にする
フォームを Alt + Tab ダイアログに表示しない
フォームを移動・サイズ変更できないようにする
フォームを水平または垂直方向にのみサイズ変更可能にする
ホットキーを使用する
レイヤード ウィンドウを使用する
ボタン の縮小ボタン
コマンド リンクを作成する
ボタンを独自の形状で描画する
ボタンにシールド アイコンを表示する
ボタンにショートカットキーを割り当てる
ボタンの 2 度押しを防止する
分割ボタンを作成する
ラベル の縮小ラベル
ラベルの背景を透過する
クラスライブラリ の縮小クラスライブラリ
アラートウィンドウ
アニメーション
ドキュメント の縮小ドキュメント
コントロールのスタイル (ControlStyles) 一覧
コントロールの属性
コントロールを描画する
フォームの FormBorderStyle によるウィンドウ スタイル一覧
ソフトウェア の縮小ソフトウェア
Mail Remocon の縮小Mail Remocon
プラグイン作成方法
ワンタッチ キーボード の縮小ワンタッチ キーボード
コマンド一覧
コマンド作成方法
コマンドの機能紹介 の縮小コマンドの機能紹介
ランチャー (特殊フォルダに対応)、フォルダ階層のメニュー表示
エアロスナップを XP, Vista で再現、7 で拡張
ウィンドウの最大化サイズを変更
マウスでウィンドウを切り替える
マウス ホイールでウィンドウを透明化
移動中のウィンドウを透明化
マウス カーソル位置のスクロール
エクスプローラーを履歴から表示
IME の状態を表示
ID、パスワードなどの自動入力
マウスをキーボードで操作
デスクトップのスナップショットを見ながら作業
クリップボードの履歴から設定・貼り付け
複数のクリップボードを使用する
定型文を入力
選択範囲を Web 検索
マルチディスプレイ環境で全画面または画面毎に壁紙を設定
定期的に壁紙を変更
シャットダウンなどの終了オプション
音量をキーボードで変更
音量をマウスで変更
ヘルプ の縮小ヘルプ
OS 起動時にワンタッチキーボードを起動する方法
ワンタッチ キーボード画面の使い方
ワンタッチ キーの追加の仕方
追加ボタンが表示されないときの対処法
コマンドを簡単に追加する方法
ワンタッチ キーの編集の仕方
ワンタッチ キーの削除の仕方
ワンタッチ キーのショートカットを作成する方法
ワンタッチ バーの使い方
プラグイン フォルダの設定方法
プラグインの更新の仕方
リンク
連絡する
ブログ

ボタンの 2 度押しを防止する

概要

ボタンの 2 度押しを防止する方法を紹介します。

対象コントロール

  • System.Windows.Forms.Button
  • System.Windows.Forms.ToolStripButton

解説

ボタンの 2 度押しを防止する二種類の方法を紹介します。

1. クリックされたボタンのみ 2 度押しを防止する方法

この方法ではボタンのクリック処理中にそれ以外のコントロールをクリックすると、そのコントロールは処理終了後にクリックされます。それも防止したいなら次の項目の方法を参照してください。

ボタンの 2 度押し判定用にフラグを用意します。ボタンがクリックされたらフラグを立てクリック処理をします。フラグが立っていたら処理を抜けます。Application.Idle イベントでフラグをおろします。

Application.Idle イベントは、簡単に説明すると他のイベントが全て終了した後に毎回発生します。Click イベントの中に再度クリックした場合は、全ての Click イベントが終了したら 1 度だけ発生します。今回の処理を行うのにベストなタイミングで発生するイベントです。

Private buttonProcessing As Boolean

Public Sub New()
    AddHandler System.Windows.Forms.Application.Idle, AddressOf Application_Idle
End Sub

Private Sub Application_Idle(ByVal sender As Object, ByVal e As System.EventArgs)
    Me.buttonProcessing = False
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    ' クリック処理中は処理を抜ける
    If Me.buttonProcessing = True Then
        Return
    End If

    Me.buttonProcessing = True

    ' Click イベント処理
End Sub
private bool buttonProcessing;

public Form1() // Form のコンストラクタ {
    System.Windows.Forms.Application.Idle += new EventHandler(Application_Idle);
}

private void Application_Idle(object sender, System.EventArgs e) {
    this.buttonProcessing = false;
}

private void button1_Click(object sender, System.EventArgs e) {
    // クリック処理中は処理を抜ける
    if (this.buttonProcessing == true) {
        return;
    }

    this.buttonProcessing = true;
    
    // Click イベント処理
}

このフラグを他のコントロールと共有すれば、それぞれのコントロール間で 2 度押しを防止できます。しかしそれをやると Click イベントがフラグだらけになってしまうため、その場合は次の方法をお勧めします。

2. ボタンのクリック処理中に他のコントロールも含めて、全てのクリックを防止する方法

ボタンをクリックした時にそれがイベントとして発生する前に、メッセージが送られてきます。メッセージが処理されるとイベントが発生します。クリック処理中に再度クリックした場合などはメッセージがキューに溜まっていきます。クリック処理が終了した後にキューにメッセージがあればそれが処理されるため、2 度押しの原因になります。つまりクリック処理の最後に保留されているメッセージを全て削除すれば、2 度押しを防止できます。

保留されているメッセージを削除するには、PeekMessage 関数を使用します。PeekMessage 関数の第 5 引数に PM_REMOVE (1) を指定することで保留状態のメッセージを削除できます。WM_PAINT メッセージは PeekMessage 関数で削除できません。削除されると描画がおかしくなるため、WM_PAINT メッセージは削除しないで DispatchMessage 関数で処理します。(PeekMessage 関数、DispatchMessage 関数は Win32API です。詳細は MSDN などを参照してください)

メッセージを削除するときは Click イベントを使用するか OnClick メソッドをオーバーライドします。

Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
    MyBase.OnClick(e)

    Dim msg As MSG
    While PeekMessage(msg, 0, 0, 0, PM_REMOVE)
        Select Case msg.msg
            Case WM_PAINT
                DispatchMessage(msg)
        End Select
    End While
End Sub
protected override void OnClick(EventArgs e)
{
    base.OnClick(e);

    MSG msg;
    while (PeekMessage(out msg, 0, 0, 0, PM_REMOVE))
    {
        switch (msg.msg)
        {
            case WM_PAINT:
                DispatchMessage(out msg);
                break;
        }
    }
}

ソースコード

Button コントロールの 2 度押しを防止するサンプルを紹介します。

このサンプルでは「ボタンのクリック処理中に他のコントロールも含めて、全てのクリックを防止する方法」を使用しています。

2 度押しを許可するかどうかを示す AllowDoubleClick プロパティを追加しています。

WM_PAINT メッセージは削除しないで処理しています。WM_LBUTTONUP と WM_MOUSELEAVE はボタンの上にマウスがある時のホバー色を解除するために処理しています。

Imports System
Imports System.ComponentModel
Imports System.Runtime.InteropServices

Namespace Extentions

    Public Class DoubleClickPreventionButton
        Inherits System.Windows.Forms.Button

        Public Sub New()
            Me._AllowDoubleClick = True
        End Sub

        Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
            MyBase.OnClick(e)

            ' 2 度押しを許可しない
            If Me.AllowDoubleClick = False Then
                Dim msg As MSG
                While PeekMessage(msg, 0, 0, 0, PM_REMOVE)
                    Select Case msg.msg
                        Case WM_PAINT, WM_LBUTTONUP, WM_MOUSELEAVE
                            DispatchMessage(msg)
                    End Select
                End While
            End If
        End Sub

        Private _AllowDoubleClick As Boolean
        <Category("動作")> _
        <DefaultValue(True)> _
        <Description("2 度押しを許可するかどうかを示します。")> _
        Public Property AllowDoubleClick() As Boolean
            Get
                Return Me._AllowDoubleClick
            End Get
            Set(ByVal value As Boolean)
                Me._AllowDoubleClick = value
            End Set
        End Property

#Region " P/Invoke "

        Private Const PM_NOREMOVE As Integer = 0
        Private Const PM_REMOVE As Integer = 1

        Private Const WM_PAINT As Integer = &HF
        Private Const WM_LBUTTONUP As Integer = &H202
        Private Const WM_MOUSELEAVE As Integer = &H2A3

        <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
        Private Shared Function PeekMessage( _
            ByRef lpMsg As MSG, _
            ByVal hWnd As Integer, _
            ByVal wMsgFilterMin As Integer, _
            ByVal wMsgFilterMax As Integer, _
            ByVal wRemoveMsg As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function

        <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
        Private Shared Function DispatchMessage( _
            ByRef lpMsg As MSG) As IntPtr
        End Function

        <StructLayout(LayoutKind.Sequential)> _
        Public Structure MSG
            Public hWnd As IntPtr
            Public msg As Integer
            Public wParam As IntPtr
            Public lParam As IntPtr
            Public time As Integer
            Public pt As POINTAPI
        End Structure

        <StructLayout(LayoutKind.Sequential)> _
        Public Structure POINTAPI
            Public x As Integer
            Public y As Integer
        End Structure

#End Region

    End Class

End Namespace
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Extentions
{
    class DoubleClickPreventionButton : System.Windows.Forms.Button
    {
        public DoubleClickPreventionButton() {
            this._AllowDoubleClick = true;
        }

        protected override void OnClick(EventArgs e) {
            base.OnClick(e);

            // 2 度押しを許可しない
            if (this._AllowDoubleClick == false) {
                MSG msg;
                while (PeekMessage(out msg, 0, 0, 0, PM_REMOVE)) {
                    switch (msg.msg) {
                        case WM_PAINT:
                        case WM_LBUTTONUP:
                        case WM_MOUSELEAVE:
                            DispatchMessage(out msg);
                            break;
                    }
                }
            }
        }

        private bool _AllowDoubleClick;
        [Category("動作")]
        [DefaultValue(true)]
        [Description("2 度押しを許可するかどうかを示します。")]
        public bool AllowDoubleClick {
            get { return this._AllowDoubleClick; }
            set { this._AllowDoubleClick = value; }
        }

        #region P/Invoke

        private const int PM_NOREMOVE = 0;
        private const int PM_REMOVE = 1;

        private const int WM_PAINT = 0xF;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_MOUSELEAVE = 0x2A3;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool PeekMessage(
            out MSG lpMsg,
            int hWnd,
            int wMsgFilterMin,
            int wMsgFilterMax,
            int wRemoveMsg
        );

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr DispatchMessage(
             out MSG lpMsg
        );

        [StructLayout(LayoutKind.Sequential)]
        public struct MSG
        {
            public IntPtr hWnd;
            public int msg;
            public IntPtr wParam;
            public IntPtr lParam;
            public int time;
            public POINTAPI pt;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINTAPI
        {
            public int x;
            public int y;
        }

        #endregion
    }
}