問題
C#において、ページスクロールボタンに対応する処理が以下のようなケース
・ボタンコントロールを非活性
・ページデータを取得して表示(1秒弱)
・ボタンコントロールを活性化
ボタン連打で後続のイベントが無視されず、連打回数分のスクロールが実行されてしまいます。
ネットで検索してもいまいち自分好みの対応方法が見つかりません。
解決策
で、ひねり出したのが以下のコードです。
/// 保留されているイベントを破棄してからコントロールのEnabledをtrueにする。
public void EnableControl(Control co)
{
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(DoEnable));
thread.Start(new object[] { co });
}
private void DoEnable(object args)
{
Control co = ((object[])args)[0] as Control;
if (co.InvokeRequired)
{
// メッセージキューの最後に当関数を呼び出す処理を追加する
co.Invoke((MethodInvoker)delegate {
DoEnable(args);
});
return;
}
// このタイミングでは保留されていたイベントはすべて破棄されているはず
co.Enabled = true;
}
呼び出し例
private void btnNext_Click(object sender, EventArgs e) {
Cursor.Current = Cursors.WaitCursor;
this.Enabled = false;
try
{
NextPage();
}
finally
{
//this.Enabled = true;
EnableControl(this);
this.Cursor = Cursors.Default;
}
}
解説?
なぜボタン連打で後続のイベントが無視されなかったか(推測)
・クリックイベントはとりあえずキューに登録される。
・イベント処理は順番に呼び出される
・ボタン非活性化してもイベントを破棄するのはイベント処理呼び出し直前
つまり、1つめのイベント処理で非活性化しても2つめ以後のイベントは登録され、後続のイベント実行時には1つめのイベント処理は終了している(ボタンは活性化されている)ので実行されてしまう。
コントロール.Invokeの仕掛け(推測)
・処理の呼び出しはイベントキューに登録されるのであろう
したがって、以下のような流れとなるのだろう
(1)1回目ボタンクリック処理(ボタン非活性化)
(2)2回目ボタンクリックイベント登録
(3)コントロール.Invokeの呼び出し登録
(4)1回目の処理終了
(5)2回目ボタンクリック処理(破棄)
(6)(3)で登録された処理ボタン活性化の実行
注意、マイクロソフトの正式ドキュメントでの確認はしていません。