トップ回答者
VB2005 ボタン二度押し防止方法

質問
回答
-
よしろう さんからの引用 おっと、NewメソッドでLOADイベントよりも先に処理を記述することもできるの
ですね。ちょっとまずは書いてみます。(VB2005 Windows Formアプリケーション)
New メソッドではなくてコンストラクタと言います。
そこではなくて、やはりエントリ ポイントでの記述が望ましいです。
エントリ ポイントを書くには、プロジェクトのプロパティから 「スタートアップ」 を Sub Main にしなくてはなりません。なお VB2005 の場合は、アプリケーション フレームワークで二重起動の防止がノンコーディングでできます。
VB2005 をお使いのようですので、アプリケーション フレームワークに頼られた方がよろしかろうと思います。
すべての返信
-
正解といっても、一人の万能な神がいて決めるわけではありませんからね。(^^;
http://www.microsoft.com/japan/msdn/community/gdn/ShowPost-38117.htm
たとえばここにいろいろな方法がありますが、この方法はここがいや、とかよしろうさんが言うことから
始めるしかないかと。
-
よしろう さんからの引用 Windows Formのボタンの二度押し防止方法ですが、別フォーラムでも同トピック、議論はあるものの
正解らしい回答を見たことがありません。変数でENABLE/DISABLEにするという案はありますが通常はどのような処理がよいのでしょうか?
見た目どうするのか、という話もありますよね。
非活性表示するだけで良いのか、モーダルで何らかの情報を表示するのかなど。
Windows Form だとよほどのことがない限りは非活性にして即更新して終了って感じですね。
現実問題確認用の MessageBox を 1 つ挟むだけでも連続で押されてしまうという誤動作は防げます。
重要なものであればフラグに頼るが確実だと思います。 -
以下なんかが参考になるんじゃないでしょうか? 結構、目から鱗でした。
無効中のボタンをマウスクリックイベントを受けてしまう
http://forums.microsoft.com/msdn-ja/ShowPost.aspx?PostID=717271&SiteID=7 -
いまさらですが。
Application.DoEventsを使う手法は、私はよくないと思います。
イベントドリブン方式のプログラミングの基本を考えれば分かると思うのですが、
イベント中にメッセージループを回すのは危険がたくさんあります。
内部で何がおこっているのか、きちんと理解して使うなら問題ないのですが、
Windowsのコードは公開されていませんので、
中で何が起こっているのか調べようがありませんし、
ある程度検証してもバージョンが変わったら変わる可能性もあります。
本来、Application.DoEventsは無くてもプログラムできるはずのものですから、
私はテストなどの一時使用を除き、一切使いません。
2度押し防止には違う方法を用いています。
「2度押しを防ぐ」という文面の解釈を文字通りとるのではなく、
「アプリケーションが処理中の場合は押しても効果が無い」と解釈し、
2度押し防止コードPrivate _IsEventProcessing As Boolean
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler Application.Idle, New EventHandler(AddressOf Application_Idle)
End SubPrivate Sub Application_Idle(ByVal sender As Object, ByVal e As System.EventArgs)
_IsEventProcessing = False
End SubPrivate Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If _IsEventProcessing Then Exit Sub
_IsEventProcessing = True
Threading.Thread.Sleep(5000) ' wait for 5secs
End Subのようにしています。
こちらのほうが、イベントドリブン式のプログラミングとしてはよりよい方法であろうと思っています。
パフォーマンス的には劣るかもしれませんが。
もちろん、
時間のかかる処理は他スレッドでやるとか、
細切れにしてTimerで行うとか、
そういったように処理できれば一番です。
-
なるほど。Application.Idleイベントをうまく使うわけですね。勉強になりました。
れい さんからの引用 イベントドリブン方式のプログラミングの基本を考えれば分かると思うのですが、
イベント中にメッセージループを回すのは危険がたくさんあります。
私もDoEventsの実装を知っているわけではありません。ただ、おっしゃられるようにDoEventsはイベント中に他のイベントを処理するために存在します。MSDNにも堂々とそのような趣旨が書いてあります。そこまで書いてあるので私は今のところDoEventsを信用しています。
すっきりしていて確かに堅いのはApplication.Idleイベントを使う方法だと思うのですが、だからと言ってDoEventsを使うなと言い切るだけの材料を私は持っていません。特に今回のケースですと、Button1のClickイベントプロシージャでButton1のEnabledをtrueに戻すだけの処理を残してでのDoEventsですから、DoEventsによってそれ以降の処理が一時的にブロックされてもあまり危険がないように思えます。違うかなぁ・・・?
-
ながーい投稿になってしまいました。
私もDoEventsの実装を知っているわけではありません。ただ、おっしゃられるようにDoEventsはイベント中に他のイベントを処理するために存在します。MSDNにも堂々とそのような趣旨が書いてあります。そこまで書いてあるので私は今のところDoEventsを信用しています。
VB6の時はこれがないとできないこともありましたし、
今でも無いと困る人たちもたくさんいるでしょう。
メッセージループを回す、という意味ではきちんと動作するのも知っています。
ですが、私はあらゆる場合でDoEventsは使わないほうが良い、と思っています。
すっきりしていて確かに堅いのはApplication.Idleイベントを使う方法だと思うのですが、だからと言ってDoEventsを使うなと言い切るだけの材料を私は持っていません。特に今回のケースですと、Button1のClickイベントプロシージャでButton1のEnabledをtrueに戻すだけの処理を残してでのDoEventsですから、DoEventsによってそれ以降の処理が一時的にブロックされてもあまり危険がないように思えます。違うかなぁ・・・?
今回はたしかにEnabled=trueにするだけなのでよさそうですが、
本当は、廃棄されたコントロールに対して、Enable=trueを行っても大丈夫かどうか、
そのコントロールを含む親コントロールが廃棄中だったり、EnableやVisibleが変更されてる最中だったら大丈夫かどうか、
そのコントロールを含むWindowが廃棄中だったり、アプリケーションが終了中だったら大丈夫か、
ということまで考慮しなければならないと、私は思います。
DoEventsを一回呼んだら、どんなメッセージが、いくつ処理されるのかわかりません。
そこまで考慮してるでしょうか?
私がDoEventsを嫌いな理由、他人に使わないほうがいいという理由を、
いま思いついたまま適当に列挙してみます。
○ 普段気にしなくてもいい、Windows Formなどの実装、振舞いを詳しく知らないといけない。
Windowsメッセージもいろいろあり、System.Windows.Forms.Controlにもいろいろなイベントがありますが、
それらはイベントがイベントを呼ぶ形で、連鎖的にイベントが呼ばれようになっています。
自分でイベントハンドラを記述し、その内部でメッセージループを回すと、その順番や内包関係が崩れます。
異常な振舞いを起こす場合もあり、対応するのが困難です。
一見動いているようにみえても、ある条件でだめになったり、デバッグも大変です。
DoEventsを使ったら、そのDoEvents内で、いろいろなイベントがいくつも起きるわけです。
他のClickイベント、Focusイベント、MouseDownイベント、
FormClosingイベント、FormCloseイベント、Disposeイベント…。
それら全てで問題がないことを確認するのは大変困難です。
例えば、
KeyDownイベント内でDoEventsを使ったら、
KeyUpイベントはいつどこで起こるのでしょうか?
KeyDownイベント処理中、KeyDownイベント内のDoEventsで起こるのでしょうか?
それともKeyDownイベントハンドラを抜けたあとに起きるのでしょうか?
KeyDownイベント処理中に他のボタンをクリックしたら、
そのKeyDownイベント、Clickイベント、KeyUpイベントはいつどこで起こるのでしょうか?
それらのイベントにTimerイベントが重なったりした場合、どうなるのでしょう?
KeyDownとUpの間にTimer.Tickが入ったりするのでしょうか?
Timer.Tickでフォーカスを移してたら、KeyUpイベントはどうなるのでしょう?
MFCのころがんばって調べたりしたんですが、複雑すぎて忘れてしまいましたし、
いまはそれに.Netの皮がついていますので、さらに複雑です。
私はそれらをとても覚えられません。調べきれません。
○DoEventsの方が綺麗にできる、という事例を見たことがない。
綺麗とは、見た目とか、保守性とか、そういったもの全てを含めてます。
とくに、DoEventsを利用したコードは保守性が激しく悪くなります。
理由は前述のように、イベント間の順序関係、依存関係のせいです。
なにかイベントを追加するたび、変更するたびに、
そのコントロールのすべてのDoEvents内から呼ばれる可能性を考慮しなければいけません。
○動作の保証ができない。
上記二つに関連して。
あるイベント中にDoEventsした場合、イベント処理中に他のイベントが発生するわけですが、
これがきちんと動作するのかわかりません。
Clickイベント処理中にClickイベントが起きても、経験上大丈夫ですが、
SelectedIndexChangedイベント中にFormClosedが起きたらたぶん例外を吐きます。
これらの動作がきちんと動くべきであるのか、動かなくても普通なのか、MSDNなどに明記されていません。
動かないイベントが何であるか分かれば対応もできますが、
どこにも書いてないので、実際やってみてエラーがでないことを確認するしかありませんが、
全てのイベントを試すのは困難です。
○スタックをたくさん使う。
そのまんま。スタックがたくさん消費されて嫌です。
○ 代替の方法がある。
説明がめんどくさいですが、Idleイベントやフラグを組み合わせれば、
DoEventsがなくても理論上大丈夫です。
○ 乱用を生む
DoEventsを使い始めると、乱用しはじめる人がいます。
本来Timerやマルチスレッドで解決すべき問題をDoEventsでごまかし始め、
そのうち、イベントドリブンとは全くかけ離れたコードになってしまうことが多々あります。
そんなとこです。
ずいぶん長くなりました。
強制する権限もないですし、
私は発言力の大きい有名人というわけでもないですし、
そう思ってる人もいるという、参考程度にしていただければ。