シングルクリックとダブルクリックの識別して、片方のみ実行するためのクラス

http://msdn.microsoft.com/ja-jp/library/vstudio/ms171543.aspx
ダブルクリックを行うと、マウスイベントとしては「シングルクリックイベント」→「ダブルクリックイベント」という順でイベントが発生する。しかし、ダブルクリックをしたときはシングルクリックイベントを発生させず、ダブルクリックイベントのみを発生させたいときがある。
この動作の実現のためには2通りの方法が存在するので、両方の方法をコードで実装した。
・最初のクリック時にシングルクリックのイベントを実行し、ダブルクリック時にその動作を取り消すという方法。
最初のクリック時に即座に操作が反映されるが、「動作を取り消す」ことが可能な操作を行う場合でないとこの方法を利用することはできない。(例えば、モーダルウィンドウを出す動作などには使えない)
JustTimeSingleClickイベントとして実装した。
・最初のクリック後、ダブルクリックになる可能性がある時間まで待ってから、シングルクリックのイベントを実行する方法。
この方法であればイベントで実行される処理内容に制限は無い。しかし、ダブルクリックになる可能性がある時間(通常は500ミリ秒)ほど操作にラグが生じてしまう。
TimeoutSingleClickイベントとして実装した。

使い方

private void Form1_Load(object sender, EventArgs e)
{
    _oSingleOrDoubleClick = new SingleOrDoubleClick();
    _oSingleOrDoubleClick.TimeoutSingleClick += new EventHandler<MouseEventArgs>(_oSingleOrDoubleClick_TimeoutSingleClick);
    _oSingleOrDoubleClick.JustTimeSingleClick += new EventHandler<JustTimeMouseEventArgs>(_oSingleOrDoubleClick_JustTimeSingleClick);
    _oSingleOrDoubleClick.DoubleClick += new EventHandler<MouseEventArgs>(_oSingleOrDoubleClick_DoubleClick);
}

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
    _oSingleOrDoubleClick.MouseDown(sender, e);
}

void _oSingleOrDoubleClick_JustTimeSingleClick(object sender, JustTimeMouseEventArgs e)
{
    Console.WriteLine("JustSingleClick");
    e.CancelAction = delegate () { Console.WriteLine("Cancel"); };
}

void _oSingleOrDoubleClick_TimeoutSingleClick(object sender, MouseEventArgs e)
{
    // JustTimeSingleClickと同時に有効にすると、1度のクリックで2回シングルクリックイベントが発生してしまう
    // Console.WriteLine("SingleClick");
}

void _oSingleOrDoubleClick_DoubleClick(object sender, MouseEventArgs e)
{
    Console.WriteLine("DoubleClick");
}

SingleOrDoubleClick.cs

/// <summary>
/// 即時実行してダブルクリック時にはキャンセルする
/// シングルクリック処理のイベント
/// </summary>
public class JustTimeMouseEventArgs : MouseEventArgs
{
    /// <summary>
    /// イベントハンドラ内で設定する。
    /// ダブルクリックされた場合は設定されたCancelActionを実行して、
    /// シングルクリックの処理内容を取り消す。
    /// </summary>
    public Action CancelAction { get; set; }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public JustTimeMouseEventArgs(MouseEventArgs e)
        : base(e.Button, e.Clicks, e.X, e.Y, e.Delta)
    {
        this.CancelAction = delegate() { };
    }
}

/// <summary>
/// シングルクリックとダブルクリックのどちらか片方のみ実行する
/// </summary>
public class SingleOrDoubleClick
{
    private int _nDoubleClickTime = SystemInformation.DoubleClickTime;      // ダブルクリックを認識する時間
    private Size _oDoubleClickSize = SystemInformation.DoubleClickSize;     // ダブルクリックを認識するサイズ
    private Timer _oClickWaitTimer;                                         // ダブルクリック待ちタイマー
    private Rectangle _oClickWaitRect;                                      // ダブルクリック待ち範囲
    private object _oSingleClickSender;                                     // クリック時の情報保持
    private MouseEventArgs _oSingleClickMouseEvent;                         // クリック時の情報保持

    private Action _oCancelAction = null;                                   // 即時処理されるシングルクリックの取消処理

    /// <summary>
    /// シングルクリック時に即時実行して、ダブルクリック時にはキャンセルする動作
    /// </summary>
    public event EventHandler<JustTimeMouseEventArgs> JustTimeSingleClick;

    /// <summary>
    /// シングルクリック後、ダブルクリックが行われないと判定されるまで待ってから処理する動作
    /// </summary>
    public event EventHandler<MouseEventArgs> TimeoutSingleClick;

    /// <summary>
    /// ダブルクリック動作
    /// </summary>
    public event EventHandler<MouseEventArgs> DoubleClick;

    /// <summary>
    /// MouseDown発生時に呼び出す
    /// </summary>
    /// <param name="e"></param>
    public void MouseDown(object sender, MouseEventArgs e)
    {
        if (IsDoubleClick(e) == false)
        {
            // シングルクリック
            JustTimeMouseEventArgs oEvnet = new JustTimeMouseEventArgs(e);
            OnJustTimeSingleClick(sender, oEvnet);
            _oCancelAction = oEvnet.CancelAction;
            StartClickWaitCheck(sender, e);
        }
        else
        {
            // ダブルクリック
            StopClickWaitCheck();
            if (_oCancelAction != null) { _oCancelAction(); }
            OnDoubleClick(sender, e);
        }
    }

    /// <summary>
    /// ダブルクリック待ちの時間が経過したときに実行
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void DoubleClickTimer_Tick(object sender, EventArgs e)
    {
        StopClickWaitCheck();
        OnTimeoutSingleClick(_oSingleClickSender, _oSingleClickMouseEvent);
    }

    /// <summary>
    /// ダブルクリック待ちを開始
    /// </summary>
    private void StartClickWaitCheck(object sender, MouseEventArgs e)
    {
        // 待ち時間設定
        StopClickWaitCheck();
        _oClickWaitTimer = new Timer();
        _oClickWaitTimer.Tick += new EventHandler(DoubleClickTimer_Tick);
        _oClickWaitTimer.Interval = _nDoubleClickTime;
        _oClickWaitTimer.Start();

        // クリック範囲設定
        _oClickWaitRect = new Rectangle(
            new Point(e.X - _oDoubleClickSize.Width / 2, e.Y - _oDoubleClickSize.Height / 2),
            _oDoubleClickSize);

        // クリック時の情報保持
        _oSingleClickSender = sender;
        _oSingleClickMouseEvent = e;
    }

    /// <summary>
    /// ダブルクリッククリック待ち終了
    /// </summary>
    private void StopClickWaitCheck()
    {
        if (_oClickWaitTimer != null)
        {
            _oClickWaitTimer.Stop();
            _oClickWaitTimer.Dispose();
            _oClickWaitTimer = null;
        }
    }

    /// <summary>
    /// ダブルクリッククリック判定
    /// </summary>
    private bool IsDoubleClick(MouseEventArgs e)
    {
        if (_oClickWaitTimer == null || _oClickWaitTimer.Enabled == false)
        {
            return false;
        }

        if (_oClickWaitRect.Contains(e.Location) == false)
        {
            return false;
        }

        return true;
    }

    #region "■イベント呼び出し"

    private void OnJustTimeSingleClick(object sender, JustTimeMouseEventArgs e)
    {
        if (JustTimeSingleClick != null) { JustTimeSingleClick(sender, e); }
    }

    private void OnTimeoutSingleClick(object sender, MouseEventArgs e)
    {
        if (TimeoutSingleClick != null) { TimeoutSingleClick(sender, e); }
    }

    private void OnDoubleClick(object sender, MouseEventArgs e)
    {
        if (DoubleClick != null) { DoubleClick(sender, e); }
    }

    #endregion
}