C#

【C#】コントロールのイベントを一時的に無効化する方法

イベントを一時的に無効にする

こんにちは、リバティエンジニア[?]の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行目)を使ってイベントを無効化している間にする処理を記述しました。

 

ラムダ式については、下記の記事にまとめているので合わせて読んでみてください。

>> [C#] ラムダ式はこれさえ見ればOK!簡単徹底解説

 

使い方はわかったのですが、もし配置しているコントロール全部を一時無効にしたい場合は少し面倒くさいですね。。
安心してください。Listに入れるコントロールに"this"を入れるとフォームに配置されたすべてのコントロールを制御できますよ!

 

// すべてのコントロールのイベントを無効化
List<Control> controlList =newList<Control>{this};

// 関数呼び出し
WindowsFormExtended.DoSomethingWithoutEvents(
    controlList,
    () => textBox1.Text = "イベントを無効化している間に値を代入する。");

 

このように"this"を指定すると、フォームに配置されたすべてのコントロールのイベントを一時無効化できます。

 

まとめ

今回は何かの処理をしている間だけ、コントロールのイベントを無効化させる方法についてみていきました。

記載したソースコードのクラスをこのままコピペして使えるので、便利だと思います。

ですが、ちゃんと一通り1度は目を通しておいてくださいね!

 

 

Kindle Unlimitedで無料で本が読める

 

Kindle Unlimitedを無料で試す

 

なら最初の30日間は無料でコーディング・デザイン・ブログ・英語・Youtubeの参考書・キャリア・副業・マーケティング・漫画・美容知識・雑誌などが読み放題です。

人気の本もあるので是非お試ししてみてください!30日以内に解約すればお金は全くかかりません。

 


最近の投稿

【今すぐ行動しよう】フリーランスエンジニアとブログの相性は抜群!

フリーランスエンジニアがスキルや経験から得たビジネスノウハウを共有する手段として「ブログ」は非常におすすめです。自身のスキルや知識のアウトプットや、実際に自分が経験したことを共有して同じような境遇の人の手助けになるような情報を公開することで自分に帰ってくるメリットは非常に多いです。今回はそのあたりについて紹介していきます。

【経験談】フリーランスのエンジニアは本当に大変なの?安定のコツやポイント

フリーランスのエンジニアは「大変だからやめとけ」という話をよく聞くので、そのことについて現役でフリーランスエンジニアとして生活している私が、その話の実態を解説しようと思います。フリーランスエンジニアの大変なところはもちろんありますが、それ以上にメリットが多いので私個人的には非常におすすめしています。

【幸福度UP】フリーランスエンジニアの「田舎暮らし」実現できます

フリーランスエンジニアとして田舎で暮らす人が増えてるって聞くけど、本当なのかな?どうやったら実現できるのか?今回はフリーランスエンジニアとして田舎で生活をしたいという方向けに、実現可能なのか、実現するにはどうすれば良いのかをご紹介します。

【単価交渉で収入アップ】フリーランスが単価交渉を成功させる4つのポイント

フリーランスが収入をアップさせるための重要な手段のひとつに「単価交渉」があります。今回は、現役でフリーランスとしてエンジニアをしている私が実践して効果のあった単価交渉の方法やコツをご紹介します。

【 今なら無料!】タスク管理ツール「Backlog」を無料で使う方法と手順

今回は、普段のタスク管理からプロジェクトの管理まで幅広く使えるツール「Backlog」を無料で使うための方法とその手段を画像つきで紹介しました。もし気に入らなければ、期間内に解約してしまえば費用はかかりません。また、以外と知られていない「フリープラン」も紹介しているので、是非参考にしてみてください。

Copyright© FUNA BLOG , 2021 All Rights Reserved.