フォームでマウスのシングル クリック イベントとダブル クリック イベントの両方を処理している状況があります。どちらの場合も何かをロードする必要がありますが、ダブルクリックが発生したときに、シングル クリック イベントに関連付けられたコードを実行したくありません。
マウス クリックをインターセプトし、ダブルかシングルかを確認してから、適切なイベントを適切に実行する方法はありますか?
おそらく、ウィンドウの WndProc をインターセプトするか何かでしょうか?
フォームでマウスのシングル クリック イベントとダブル クリック イベントの両方を処理している状況があります。どちらの場合も何かをロードする必要がありますが、ダブルクリックが発生したときに、シングル クリック イベントに関連付けられたコードを実行したくありません。
マウス クリックをインターセプトし、ダブルかシングルかを確認してから、適切なイベントを適切に実行する方法はありますか?
おそらく、ウィンドウの WndProc をインターセプトするか何かでしょうか?
いいえ、タイムマシンがなければ、それはほとんど不可能です。そして、Windowsがダブルクリックとシングルクリックをどのように区別するかを理解すれば、それは実際には意味がありません。
Raymond Chen(Windows Shellチームの開発者)は、「Windowsがシングルクリックをダブルクリックに変換する方法の論理的帰結」というタイトルのブログエントリで正確に説明していることがわかりました。
具体的には、関数で指定された間隔内に2回目のクリックが発生したため、Windowsは何かをダブルクリックとして解釈することしか認識していません。何かがシングルクリックになるのかダブルクリックになるのかを事前に判断するには(レイモンドが雄弁に言っているように)透視が必要になるため、ウィンドウマネージャーは先に進み、最初のクリックが受信されるとすぐにメッセージを送信します。メッセージは、2回目のクリックが実際にダブルクリックを表すことが確認された後、後でのみ送信されます。結果として、アプリケーションは、受信したメッセージごとに常に2つのメッセージを受信します。GetDoubleClickTime
WM_LBUTTONDOWN
WM_LBUTTONDBLCLK
WM_LBUTTONDOWN
WM_LBUTTONDBLCLK
これで、.NET Frameworkの設計者は、このプロセスがどのように機能するかを理解し、それに応じて発生するイベントを設計しました。もちろん、ダブルクリックの前に常に発生するシングルクリックについては何もできませんが、ユーザーがダブルクリックイベントの一部であると判断された場合は、セカンドクリックメッセージを抑制することができました。Control.MouseClick
イベントのドキュメント(WM_LBUTTONDOWN
メッセージにほぼ対応)には、次のように書かれています。
ユーザーのオペレーティングシステムのマウス設定によって決定されるように、十分に近い時間に発生する2回のシングルクリック
MouseDoubleClick
は、2番目のイベントの代わりにイベントを生成しMouseClick
ます。
ただし、上記でリンクしたRaymondのブログ記事では、ダブルクリックアクションがシングルクリックアクションとは無関係である設計を主張するアプリや開発者向けの可能な回避策を提案しています(ただし、どちらも実際にこれを行うことはお勧めしません) )::
ここで、ダブルクリックアクションをシングルクリックアクションとは無関係にするという疑わしい設計を継続したいプログラムであるとします。職業はなんですか?
さて、あなたができることの1つは、ミリ秒単位で起動するよう
WM_LBUTTONDOWN
にタイマーを設定する以外は、メッセージの受信時に何もしないことです。GetDoubleClickTime()
[午前10時修正]WM_LBUTTONDBLCLK
その時間内にメッセージが表示された場合は、結局ダブルクリックでした。そうでない場合は、シングルクリックである必要があるため、シングルクリックアクションを実行できます(少し遅れますが)。
これは、あなたが要求したことを行うためのコードです。
Public Class Form1
Const WM_LBUTTONDOWN As Integer = &H201
Const WM_LBUTTONDBLCLK As Integer = &H203
Private WithEvents tmrDoubleClicks As Timer
Dim isDblClk As Boolean
Dim firstClickTime As Date
Dim doubleClickInterval As Integer
Sub New()
' This call is required by the designer.
InitializeComponent()
tmrDoubleClicks = New Timer
' Add any initialization after the InitializeComponent() call.
tmrDoubleClicks.Interval = 50
doubleClickInterval = CInt(Val(Microsoft.Win32.Registry.CurrentUser.
OpenSubKey("Control Panel\Mouse").
GetValue("DoubleClickSpeed", 1000)))
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
If disposing AndAlso tmrDoubleClicks IsNot Nothing Then
tmrDoubleClicks.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Select Case m.Msg
Case WM_LBUTTONDOWN
If Not isDblClk Then
firstClickTime = Now
tmrDoubleClicks.Start()
End If
Case WM_LBUTTONDBLCLK
isDblClk = True
tmrDoubleClicks.Stop()
DoubleClickActivity()
isDblClk = False
Case Else
MyBase.WndProc(m)
End Select
End Sub
Private Sub DoubleClickActivity()
'implement double click activity here
Dim r As New Random(Now.TimeOfDay.Seconds)
Me.BackColor = Color.FromArgb(r.Next(0, 255),
r.Next(0, 255),
r.Next(0, 255))
End Sub
Private Sub SingleClickActivity()
'implement single click activity here
Beep()
End Sub
Private Sub tmrDoubleClicks_Tick(ByVal sender As Object,
ByVal e As System.EventArgs
) Handles tmrDoubleClicks.Tick
If Now.Subtract(firstClickTime).TotalMilliseconds >
doubleClickInterval Then
'since there was no other click within the doubleclick speed,
'stop waiting and fire the single click activity
isDblClk = False
tmrDoubleClicks.Stop()
SingleClickActivity()
End If
End Sub
End Class
このコードの要点は、ダブルクリック時間が経過するまでクリック イベントの発生を遅らせることです。その時間内に別のクリック イベントが発生した場合、クリック イベントを呼び出さずにダブルクリック イベントが呼び出されます。ただし、ダブルクリックがない場合は、クリック イベントが呼び出されます。
この遅延は、ダブルクリックの速度が速いコンピューターで特に顕著です。一般的なコンピューターでは、ダブルクリックの速度は 500 ミリ秒であるため、コードはクリックが発生してから 500 ミリ秒から 600 ミリ秒の間にクリック イベントを実行します。