C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[13]: Enterキーによるボタン連打制御


(過去ログ 78 を表示中)

[トピック内 30 記事 (1 - 20 表示)]  << 0 | 1 >>

■45883 / inTopicNo.1)  Enterキーによるボタン連打制御
  
□投稿者/ alvin (23回)-(2010/01/21(Thu) 15:17:20)

分類:[VB.NET/VB2005 以降] 

お世話になっております。
今回はButtonをEnterキーでの連打制限について質問させていただきます。

Enterキーにより連打を制限するためにPreviewKeyDown,KeyUpを利用しています。
button1があるとします。

Private Sub PreviewKeyDownHandler(VaVal sender As Object, ByVal e As System.Windows.Form.KeyEventArgs) _
Handles button1.PreviewKeyDown
If e.KeyData = Keys.Enter Then
e.IsInputKey = True
End
End Sub

Private KeyUpHandler(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _
Handles button1.KeyUp
If e.KeyData = Keys.Enter Then
button1.PerformClick()
End If
End Sub
------------------------------------------------------
コピーペでないので、ソースにミスがあるかもしれません。
------------------------------------------------------

上記のコードで、連打制御はできていますが、問題が発生しました。

下記状態2の場合、Enterキーが押されたときに、PreviewKewDownが発生しないまま、
ボタンクリックイベントが発生します。

ボタンの選択状態1:ボタンの周りに点線のようなものがあり、選択されているように見える。
ボタンの選択状態2:ボタンの周りに点線のようなものはなく、選択されているようにみえる。

これにより下記の問題が発生します。

条件:
Form1、Form2があるとします。
Form1にはForm2を表示する表示ボタンがあり、Form2にはForm2を終了する終了ボタンがあるとします。

操作:
1.Form1で表示ボタンを押下し、Form2を表示しました。
2.Form2が表示され、終了ボタンにフォーカスが当たっています。
 !!この時、ボタンの選択状態は上記の状態2です。
3.Form2の終了ボタンにフォーカスがある状態で、Enterキーを押すしたタイミングでForm2が消え、
Form1にもどり(表示ボタンにフォーカスあり)、Enterキーを話したタイミングでForm2がまた表示されます。

つまり、Form2で終了ボタンでEnterキーを押すとForm2が閉じる→Form2が表示される動作を繰り返すようになります。

--------------------------------------------------------------------------
質問1:PreviewKeyDownが発生しない原因はなんでしょか?
質問2:Enterキーによるボタンの連打を制御する適切な方法は他にあるでしょうか?
引用返信 編集キー/
■45925 / inTopicNo.2)  Re[1]: Enterキーによるボタン連打制御
□投稿者/ たくボン (330回)-(2010/01/21(Thu) 23:30:06)
No45883 (alvin さん) に返信
> お世話になっております。
> 今回はButtonをEnterキーでの連打制限について質問させていただきます。
> --------------------------------------------------------------------------
> 質問1:PreviewKeyDownが発生しない原因はなんでしょか?
> 質問2:Enterキーによるボタンの連打を制御する適切な方法は他にあるでしょうか?

じっくり見てないけど、質問2についてはClickイベントの頭で、ボタンのEnableをfalseにしておけばいいだけのような気もする。
引用返信 編集キー/
■45927 / inTopicNo.3)  Re[2]: Enterキーによるボタン連打制御
□投稿者/ alvin (28回)-(2010/01/21(Thu) 23:42:27)
No45925 (たくボン さん) に返信
> ■No45883 (alvin さん) に返信
>>お世話になっております。
>>今回はButtonをEnterキーでの連打制限について質問させていただきます。
>>--------------------------------------------------------------------------
>>質問1:PreviewKeyDownが発生しない原因はなんでしょか?
>>質問2:Enterキーによるボタンの連打を制御する適切な方法は他にあるでしょうか?
>
> じっくり見てないけど、質問2についてはClickイベントの頭で、ボタンのEnableをfalseにしておけばいいだけのような気もする。

システム全体からみると、ボタンの数も多いし、クリックイベント毎にEnableで制御するのは効率的ではないかと。
それに、結合テスト中なので、そのような修正はできないです。
なので、各画面が継承している基底クラスで制御しないといけないです。
記述していたソースの基底クラスの一か所で実装し、制御しています。

引用返信 編集キー/
■45930 / inTopicNo.4)  Re[3]: Enterキーによるボタン連打制御
□投稿者/ なちゃ (359回)-(2010/01/22(Fri) 00:00:14)
んー、今は最初に書かれたようなコードを基底フォームに書いて処理してるってことですか?

結局今のやり方を変えられないんなら、デフォルトボタンを設定しないとかはだめですか?
あるいはフォーム側のイベントでどうにかするか。
※多分、デフォルトボタンに設定されている場合に、ボタンのプレビューイベントが発生しないってことですよね?

引用返信 編集キー/
■45931 / inTopicNo.5)  Re[3]: Enterキーによるボタン連打制御
□投稿者/ たくボン (333回)-(2010/01/22(Fri) 00:01:48)
No45927 (alvin さん) に返信
> システム全体からみると、ボタンの数も多いし、クリックイベント毎にEnableで制御するのは効率的ではないかと。
これは初耳。

> それに、結合テスト中なので、そのような修正はできないです。

これも初耳(笑)

> なので、各画面が継承している基底クラスで制御しないといけないです。

継承しているクラスがあるなら、基底クラスでEnableの制御を行ってClickイベントの代わりになるイベントを用意して発火。
継承しているクラスは、そのイベントで処理を行えば一元化できるんじゃないかな?(Clickイベントの名前とHandle部分は変更する必要があるけど、状況が状況なのでそこはプロジェクトリーダーに相談してみるのもいいと思う)
引用返信 編集キー/
■45944 / inTopicNo.6)  Re[1]: Enterキーによるボタン連打制御
□投稿者/ Jitta on the way (523回)-(2010/01/22(Fri) 07:35:47)
No45883 (alvin さん) に返信

チャタリング防止?
もしくは、押しっぱなしによるリピート防止?


んなことせずに、適切な箇所でイベント空回しすればよかったんじゃないか、と。


ところで、テスト中にバグが発見されたら、どうします?コードを修正しますよね?今組んであるコードが、予想以上に広範囲にわたってバグっていると“わかれば”、やはり修正するしかないんじゃない?
引用返信 編集キー/
■45953 / inTopicNo.7)  Re[2]: Enterキーによるボタン連打制御
□投稿者/ alvin (29回)-(2010/01/22(Fri) 10:12:36)
Enterキー押しっぱなしによるリピート防止です。

バグですとも言えないし、防止するための対策を探しているんです。

申し訳ないですが、皆さん質問1に注目していただけないでしょうか?−0−;

引用返信 編集キー/
■45957 / inTopicNo.8)  Re[1]: Enterキーによるボタン連打制御
□投稿者/ みきぬ (730回)-(2010/01/22(Fri) 10:58:19)
No45883 (alvin さん) に返信
> ボタンの選択状態2:ボタンの周りに点線のようなものはなく、選択されているようにみえる。

そのボタンはたぶん、Form の AcceptButton に指定されているんだね。


> 質問1:PreviewKeyDownが発生しない原因はなんでしょか?

PreviewKeyDown イベントは、フォーカスがあるコントロールに対して発生します。
ボタンにフォーカスがあるわけではないので発生しない、ただそれだけ。

試しに入力中のコントロールの PreviewKeyDown イベントを拾ってみればわかると思うよ。
引用返信 編集キー/
■45959 / inTopicNo.9)  Re[2]: Enterキーによるボタン連打制御
□投稿者/ alvin (31回)-(2010/01/22(Fri) 11:38:13)
2010/01/22(Fri) 11:39:26 編集(投稿者)

No45957 (みきぬ さん) に返信
> ■No45883 (alvin さん) に返信
>>ボタンの選択状態2:ボタンの周りに点線のようなものはなく、選択されているようにみえる。
>
> そのボタンはたぶん、Form の AcceptButton に指定されているんだね。
>
そのような設定をしていないのを確認しました。


>
>>質問1:PreviewKeyDownが発生しない原因はなんでしょか?
>
> PreviewKeyDown イベントは、フォーカスがあるコントロールに対して発生します。
> ボタンにフォーカスがあるわけではないので発生しない、ただそれだけ。
>
> 試しに入力中のコントロールの PreviewKeyDown イベントを拾ってみればわかると思うよ。
ボタンにフォーカス当たっているように見えます。(周りに点線がない状態)
ボタンに当たってないとしたら、ENTERキーの押下でクリックイベントが発生するのはなぜでしょうか?
AcceptButtonは設定してないのに。

フォーカスは当たってない、でも選択されているという状態ってありえるのでしょうか?



引用返信 編集キー/
■45960 / inTopicNo.10)  Re[3]: Enterキーによるボタン連打制御
□投稿者/ みきぬ (731回)-(2010/01/22(Fri) 12:57:05)
No45959 (alvin さん) に返信
> ボタンにフォーカス当たっているように見えます。(周りに点線がない状態)

たとえば Google のトップページを開いて、
検索語句を入力しているときの「Google 検索」ボタンの状態なわけですよね。
http://www.google.co.jp/

> ボタンに当たってないとしたら、ENTERキーの押下でクリックイベントが発生するのはなぜでしょうか?
> AcceptButtonは設定してないのに。

私からは「Enter キーを押したときに、そのボタンがクリックされたことにする」
という設定が何らかの形でされているからとしか言えない。

Windowsフォームだと、AcceptButton 以外にそのやり方を知らないし。
引用返信 編集キー/
■45961 / inTopicNo.11)  Re[4]: Enterキーによるボタン連打制御
□投稿者/ おのでら (2回)-(2010/01/22(Fri) 13:02:49)
おのでら さんの Web サイト
とりあえずプロジェクト全体のコードに対して「AcceptButton」で検索してみてはどうでしょうか。
引用返信 編集キー/
■45962 / inTopicNo.12)  Re[4]: Enterキーによるボタン連打制御
□投稿者/ alvin (32回)-(2010/01/22(Fri) 13:02:53)
No45960 (みきぬ さん) に返信
> ■No45959 (alvin さん) に返信
>>ボタンにフォーカス当たっているように見えます。(周りに点線がない状態)
>
> たとえば Google のトップページを開いて、
> 検索語句を入力しているときの「Google 検索」ボタンの状態なわけですよね。
> http://www.google.co.jp/
>
おお、正にこの状態です。
フォーカスはテキスト入力のとこにあるのに、検索ボタンにもフォーカスがあるように見える状態ですね。


>>ボタンに当たってないとしたら、ENTERキーの押下でクリックイベントが発生するのはなぜでしょうか?
>>AcceptButtonは設定してないのに。
>
> 私からは「Enter キーを押したときに、そのボタンがクリックされたことにする」
> という設定が何らかの形でされているからとしか言えない。
>
> Windowsフォームだと、AcceptButton 以外にそのやり方を知らないし。
こちらに関しては、もうすくし調査してみます。


引用返信 編集キー/
■45963 / inTopicNo.13)  Re[5]: Enterキーによるボタン連打制御
□投稿者/ alvin (33回)-(2010/01/22(Fri) 13:03:33)
No45961 (おのでら さん) に返信
> とりあえずプロジェクト全体のコードに対して「AcceptButton」で検索してみてはどうでしょうか。
検索済みですが、見つからないです。
引用返信 編集キー/
■45964 / inTopicNo.14)  Re[6]: Enterキーによるボタン連打制御
□投稿者/ おのでら (3回)-(2010/01/22(Fri) 13:09:52)
おのでら さんの Web サイト
>ボタンにフォーカス当たっているように見えます。(周りに点線がない状態)
>ボタンに当たってないとしたら、ENTERキーの押下でクリックイベントが発生するのはな
>ぜでしょうか?
>AcceptButtonは設定してないのに。

>フォーカスは当たってない、でも選択されているという状態ってありえるのでしょうか

Form2 上にフォーカスが受け取れるコントロールがボタンのみであれば必然的にボタンにフォーカスがあたるということはあるんですけどねー。そのあたりはどうでしょうか。
引用返信 編集キー/
■45965 / inTopicNo.15)  Re[7]: Enterキーによるボタン連打制御
□投稿者/ みきぬ (732回)-(2010/01/22(Fri) 13:17:30)
> Form2 上にフォーカスが受け取れるコントロールがボタンのみであれば必然的にボタンにフォーカスがあたるということはあるんですけどねー。そのあたりはどうでしょうか。

状況から判断しておそらくそれはないし、仮にフォーカスがあるとした場合
「PreviewKeyDown が発生しないのはなぜか?」という問題が復活します。
引用返信 編集キー/
■45966 / inTopicNo.16)  Re[7]: Enterキーによるボタン連打制御
□投稿者/ alvin (34回)-(2010/01/22(Fri) 13:17:35)
No45964 (おのでら さん) に返信
> >ボタンにフォーカス当たっているように見えます。(周りに点線がない状態)
> >ボタンに当たってないとしたら、ENTERキーの押下でクリックイベントが発生するのはな
> >ぜでしょうか?
> >AcceptButtonは設定してないのに。
>
> >フォーカスは当たってない、でも選択されているという状態ってありえるのでしょうか
>
> Form2 上にフォーカスが受け取れるコントロールがボタンのみであれば必然的にボタンにフォーカスがあたるということはあるんですけどねー。そのあたりはどうでしょうか。

現状は、画面を開くボタンの状態が「GOOGLE.CO.JPを開いたら検索ボタンの状態」と同じです。
つまり、フォーカスは当たってない(なので、PreviewKewDownが発生しない)、でもEnterキーを押すとそのボタンのクリックイベントが発生するようになっております。

引用返信 編集キー/
■45989 / inTopicNo.17)  Re[8]: Enterキーによるボタン連打制御
□投稿者/ Jitta on the way (524回)-(2010/01/22(Fri) 18:11:47)
2010/01/22(Fri) 20:20:35 編集(投稿者)

No45966 (alvin さん) に返信
> ■No45964 (おのでら さん) に返信
>>>ボタンにフォーカス当たっているように見えます。(周りに点線がない状態)
>>>ボタンに当たってないとしたら、ENTERキーの押下でクリックイベントが発生するのはな
>>>ぜでしょうか?
>>>AcceptButtonは設定してないのに。
>>
>>>フォーカスは当たってない、でも選択されているという状態ってありえるのでしょうか
>>
>>Form2 上にフォーカスが受け取れるコントロールがボタンのみであれば必然的にボタンにフォーカスがあたるということはあるんですけどねー。そのあたりはどうでしょうか。
>
> 現状は、画面を開くボタンの状態が「GOOGLE.CO.JPを開いたら検索ボタンの状態」と同じです。
> つまり、フォーカスは当たってない(なので、PreviewKewDownが発生しない)、でもEnterキーを押すとそのボタンのクリックイベントが発生するようになっております。
>

マウスのボタンやフォーム上のボタンはクリックで機能するけど、キーボードのキーは押し込みで機能するからね。キーを押し込んでいる間機能して、クリック イベントを発生させている、と。


詳細は、家に帰ってから。

なんだけど、情報少なすぎ。最初のポストには、フォームを継承しているとは書いてないよね。
他に、フォーム1からフォーム2を表示する方法。フォーム2を消す方法。ボタンのクリック イベントでやっていること。問題を確定する為には、これくらいが必要かな。


> 質問2:Enterキーによるボタンの連打を制御する適切な方法は他にあるでしょうか?
> Enterキー押しっぱなしによるリピート防止です。
 「ボタンの連打」(チャタリング)と、「押しっぱなし」は、違います。本当は、どっちですか?

 とりあえず、「押しっぱなし」と仮定して進めます。

 実際にあったことですが、BIOS の設定でキー リピート間隔を広げて、押しっぱなしに対応されている方がいらっしゃいました。特定個人のために実装する機能であれば、こういう下層での設定が、全般的な問題を解決する事にもなります。

 さて、問題を解決するためには、表面上の現象に対応するのではなく、問題が発生する原因を調べ、その原因を取り除くようにしなければなりません。現象にのみ着目し、それに対応するだけなら、原因は他の現象として現れてきます。
 では、今回の問題の原因はなんでしょう?「クリック」と「キーを押す」との機能差です。
 フォーム上の「ボタン」とマウスのボタンは、クリックする、つまりボタンを押し込んでから放すことで機能します。しかし、キーボードは、押し込むことで機能します。通常、自動販売機などのボタンも、押し込むことで機能しますよね。よって、KeyUp を見ることが、すでに無駄であると考えます。そこではもう、 Click イベントが機能してしまっています。

 そこで、「イベントを捨てればええんちゃう?(適切な箇所でイベント空回し)」と考え、Form1.button1_Click に Application.DoEvents を入れてみたのですが、ダメでした。
Click() {
  Application.DoEvents();
  this.Hide();
  (new Form2()).Show();
}

 じゃぁ、キーを押し込み続けているならイベントが発生し続けているはず。アイドルになるまで待てばいいのでは?と考えたのですが、これもダメでした。フォームが表示されるタイミングで、Idle イベントも発生していました。DoEvents がダメだった時点で、「イベント キューが空になるタイミングがある」と、考えるべきだったかも。

 おそらく、「イベントを捨てる」ではなく、「キーバッファが空になるまで待つ」であればいけると思われますが、その方法は、私は知りません。


> 質問1:PreviewKeyDownが発生しない原因はなんでしょか?
 「すでに押されている」ため、キーの状態が変化したわけではないから。キーが押されたのは、他のコントロールにフォーカスがあるときです。このイベントは、コントロールがフォーカスを持っているときにキーが押されたら、発生します。しかし、キーは“押されている間”、機能を実行し続けます。よって、Click イベントが発生することになります。ここまで、Click イベントの実装が出てきていません。Click イベントでは、どうやって押しっぱなしを防衛しているのでしょう?
 もちろん、「押しっぱなし」ではなく「連打」であるなら、この回答は的外れになります。

 「押しっぱなし」に対応するには、「ボタンにフォーカスがあるときに、キーがクリックされた」事を検出するように実装しなければならないようです。しかしこれは、「ボタンの連打」には対応できません。連打するということは、クリックが連続して多数発生していることを表すからです。

 まだ、不十分...KeyDown - KeyUp まで防御して、KeyPress で PerformClick すりゃええんじゃね?

public partial class FormBase : Form
{
  public FormBase() {
    InitializeComponent();
    downTime = new DateTime(0);
  }

  private DateTime downTime;

  private void button1_KeyUp(object sender, KeyEventArgs e) {
    DateTime upTime = DateTime.Now;
    var delta = (upTime - downTime);
    if (delta.Seconds < 1) { // 要調整
      downTime = new DateTime(0);
      button1.PerformClick();
    }
    downTime = new DateTime(0);
  }

  private void button1_Click(object sender, EventArgs e) {
    if (downTime.Ticks != 0) { return; }
    this.Hide();
  }

  private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
    downTime = DateTime.Now;
  }
}

引用返信 編集キー/
■46007 / inTopicNo.18)  Re[8]: Enterキーによるボタン連打制御
□投稿者/ alvin (36回)-(2010/01/23(Sat) 01:02:02)
Jitta on the way さん

長文ありがとうございました。

時刻による制御も可能であると思いますが、質問1の現象ですとPreviewKeyDownイベントが発生しないまま、
Enterキー押下によるClickイベントが発生してしまいます。

問題点を整理します。
メニューから画面を起動すると、ボタンにフォーカスがあるように見えるが(実際は違うような・・・)、
Enterキーを押すとPreviewKeyDown、KeyDown、KeyPress、KeyUpが発生しないまま、ボタンをClickイベントが発生してしまいます。

なぜボタンの状態がこうなる?または、なぜこのような動きをするのでしょうか?

引用返信 編集キー/
■46008 / inTopicNo.19)  Re[9]: Enterキーによるボタン連打制御
□投稿者/ おのでら (4回)-(2010/01/23(Sat) 01:59:39)
おのでら さんの Web サイト
なんか質問と回答がぐるぐるまわって終わりそうになさそうなので、今動いているプログラムから今回試す動作部分だけ抜き取って最少プログラムで動かしてみたほうがいいかもしれませんね。それで正常に動けば他のなんらかの要因で制御がおかしくなっていることがわかりますし。

ちなみに私のほうで下のような簡単なプログラムで試してみましたが「Enter押しっぱなしでも」「連打でも」PreviewKeyDownイベントは呼ばれました。

新規プロジェクトでForm1作成、ボタンA配置

Form2を作成し、ボタンB配置。Click, PreviewKeyDown, Keyup イベントをそれぞれボタンBに設定。

Form3を作成し単純にForm2を継承

Form1のボタンAの Click イベントでForm3を ShowDialog
引用返信 編集キー/
■46009 / inTopicNo.20)  Re[9]: Enterキーによるボタン連打制御
 
□投稿者/ なちゃ (363回)-(2010/01/23(Sat) 02:04:58)
ちょっと気になったので。

>> 質問1:PreviewKeyDownが発生しない原因はなんでしょか?
>「すでに押されている」ため、キーの状態が変化したわけではないから。

いや、PreviewKeyDownはキーリピート毎に発生しますよ。
そこでキーを無効にしているので、やり方や挙動の是非はとりあえずおいておいても、押しっぱなし防止自体はできているのでしょう(確認はしてませんが)。

AcceptButtonじゃないとするとちょっと原因は思いつかないんですけどね。
そうそう、45930でデフォルトボタンとか書いたのは、AcceptButtonの事(つもり)でした。
※今更遅いですが。

引用返信 編集キー/

次の20件>
トピック内ページ移動 / << 0 | 1 >>

管理者用

- Child Tree -