イベントを一時的に無効にする
こんにちは、リバティエンジニア[?]のFUNAです。 現役エンジニアとしてアプリケーション開発やWeb制作、SEOやブログ運営をしています。
今回は、「何かの処理をしている間だけコントロールのイベントを無効にするためにはどうすればよいか。」ということを見ていきましょう。
※なお、本記事は参考サイト(Windowsコントロールのイベントを一時的に無効化したい (Windowsフォーム編))でたーせる様がわかりやすいソースコードを公開されていますので、そちらを引用させていただいております。
なぜ一時的に無効にする必要があるのか?

「アプリケーション画面の起動が遅い」
この問題ですが、原因の一つとして画面のコントロールに値を設定する際、配置しているコントロールの不要なイベントが一斉に走るせいでパフォーマンスが悪化していることでした。- 「コントロールのValueを変えた際に期待した値と違う結果になる」
こちらは、例えばTextBoxコントロールの値を書き換えたときに不要なイベントが走り、期待した値とは違う値に書き換えられてしまう。

では実際にどのように実装するか見ていきましょう。
ソースコード
今回は、「ある処理中はイベントを一時無効にする」という一連の処理をクラスにまとめました。
クラス構成は、「DisableFormEvent.cs」というクラスに、Publicなメソッドはたった一つにして、シンプルにわかりやすくしました!
では、DisableFormEvent.csクラスのソースを見ていきます。
// ===========================================================================
// DisableFormEvent.cs
// コントロールイベント一時無効クラス.
// ===========================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
namespace ExtendedUtility {
public static class DisableFormEvent {
/// <summary>
/// 指定したコントロールのイベントを一時的に無効化し、処理を実行します
/// </summary>
/// <param name="control">対象コントロールの入ったList</param>
/// <param name="action">実行したいイベント</param>
public static void DoSomethingWithoutEvents (List<Control> control, Action action) {
if (control == null)
throw new ArgumentNullException ();
if (action == null)
throw new ArgumentNullException ();
foreach (var ctrl in control) {
var eventHandlerInfo = RemoveAllEvents (ctrl);
try {
action ();
} finally {
RestoreEvents (eventHandlerInfo);
}
}
}
private static List<EventHandlerInfo> RemoveAllEvents (Control root) {
var ret = new List<EventHandlerInfo> ();
GetAllControls (root).ForEach ((x) =>
ret.AddRange (RemoveEvents (x)));
return ret;
}
private static List<Control> GetAllControls (Control root) {
var ret = new List<Control> () { root };
ret.AddRange (GetInnerControls (root));
return ret;
}
private static List<Control> GetInnerControls (Control root) {
var ret = new List<Control> ();
foreach (Control control in root.Controls) {
ret.Add (control);
ret.AddRange (GetInnerControls (control));
}
return ret;
}
private static EventHandlerList GetEventHandlerList (Control control) {
const string EVENTS = "EVENTS";
const BindingFlags FLAG = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase;
return (EventHandlerList) control.GetType ().GetProperty (EVENTS, FLAG).GetValue (control, null);
}
private static List<object> GetEvents (Control control) {
return GetEvents (control, control.GetType ());
}
private static List<object> GetEvents (Control control, Type type) {
const string EVENT = "EVENT";
const BindingFlags FLAG = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;
var ret = type.GetFields (FLAG).Where ((x) =>
x.Name.ToUpper ().StartsWith (EVENT)).Select ((x) =>
x.GetValue (control)).ToList ();
if (!type.Equals (typeof (Control)))
ret.AddRange (GetEvents (control, type.BaseType));
return ret;
}
private static List<EventHandlerInfo> RemoveEvents (Control control) {
var ret = new List<EventHandlerInfo> ();
var list = GetEventHandlerList (control);
foreach (var x in GetEvents (control)) {
ret.Add (new EventHandlerInfo (x, list, list[x]));
list.RemoveHandler (x, list[x]);
}
return ret;
}
private static void RestoreEvents (List<EventHandlerInfo> eventInfoList) {
if (eventInfoList == null)
return;
eventInfoList.ForEach ((x) =>
x.EventHandlerList.AddHandler (x.Key, x.EventHandler));
}
private sealed class EventHandlerInfo {
public EventHandlerInfo (object key, EventHandlerList eventHandlerList, Delegate eventHandler) {
this.Key = key;
this.EventHandlerList = eventHandlerList;
this.EventHandler = eventHandler;
}
public object Key { get; private set; }
public EventHandlerList EventHandlerList { get; private set; }
public Delegate EventHandler { get; private set; }
}
}
}
クラス全体なので、結構な長さですね。
肝心のPublicな関数は、「DoSomethingWithoutEvents(List<Control> control, Action action)」です。
使い方としては、第一引数にイベントを停止したいコントロールの入ったListを指定し、第二引数には、イベントを無効化している間に実行したい処理を指定します。
DisableFormEventクラスの使い方
では、実際に呼び出すときの使い方を見ていきます。
// イベントを無効化したいコントロールをListに追加
List<Control> controlList =newList<Control>{textBox1, button1};
// 関数呼び出し
WindowsFormExtended.DoSomethingWithoutEvents(
controlList,
() => textBox1.Text = "イベントを無効化している間に値を代入する。");
今回はラムダ式(7行目)を使ってイベントを無効化している間にする処理を記述しました。
ラムダ式については、下記の記事にまとめているので合わせて読んでみてください。

// すべてのコントロールのイベントを無効化
List<Control> controlList =newList<Control>{this};
// 関数呼び出し
WindowsFormExtended.DoSomethingWithoutEvents(
controlList,
() => textBox1.Text = "イベントを無効化している間に値を代入する。");
このように"this"を指定すると、フォームに配置されたすべてのコントロールのイベントを一時無効化できます。
C#の学習におすすめの参考書
C#の学習において、実際に私も参考にしたことがある本をご紹介します。
まとめ
今回は何かの処理をしている間だけ、コントロールのイベントを無効化させる方法についてみていきました。
記載したソースコードのクラスをこのままコピペして使えるので、便利だと思います。
ですが、ちゃんと一通り1度は目を通しておいてくださいね!
-

【2021年最新版】C#学習におすすめ本のレベル別TOP3を紹介【現役エンジニアが厳選】
現役エンジニアがおすすめするC#の本・参考書のTOP3をご紹介!本当にお勧めした本だけを厳選したので、TOP3だけを紹介します。それぞれのレベル別に分けたので、自分に合ったものを選んでください。