BackgroundWorkerの使い方 - C#

インスタンスを生成します。

BackgroundWorker  bw = new BackgroundWorker();
ここからは「bw」という名前で生成したインスタンスを使い、話を進めます。


バックグラウンド処理を実行する


DoWorkイベントにバックグラウンドで行う処理(イベントハンドラ)をフックします。
bw.DoWork += new DoWorkEventHandler(bw_DoWork);


バックグラウンド処理を実行する。
bw.RunWorkerAsync();

引数を渡す場合の例)
bw.RunWorkerAsync(100);

DoWorkイベントハンドラ内で渡された引数を使用する例)
int arg = (int)e.Argument;

DoWorkイベントハンドラの例)
void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bw = sender as BackgroundWorker;

    int i = 0, count = (int)e.Argument;

    while (i < count)
    {
        Thread.Sleep(100);
        bw.ReportProgress(++i);
        if (bw.CancellationPending)
        {
            e.Cancel = true;
            break;
        }
    }
}



処理をキャンセルする

BackgroundWorkerが非同期のキャンセルをサポートしているかどうかを示す値を設定します。
bw.WorkerSupportsCancellation = true;

キャンセルを要求します。
bw.CancelAsync();
CancellationPendingプロパティがtrueに設定されます。

処理がキャンセルされたかをDoWorkイベントハンドラ内でCancellationPendingプロパティを定期的にチェックする必要があります。
例)
BackgroundWorker worker = sender as BackgroundWorker;
if(worker.CancellationPending)
{
    //e.Cancel = true;
}

処理がキャンセルされた場合、RunWorkerCompletedイベントで処理がキャンセルされたことを知らせるにはDoWorkイベントハンドラ内で以下の処理を行います。
e.Cancel = true;

RunWorkerCompletedイベントにフックしたイベントハンドラで処理がキャンセルされたかどうかを取得する例を示します。
if (e.Cancelled) {
    MessageBox.Show("キャンセルされました。");
}

キャンセルの流れ
bw.CancelAsync()
    ↓
DoWorkイベントハンドラにて
worker.CancellationPendingプロパティがtrueになる。
    ↓
キャンセルされたことを知らせるため
e.Cancel = true
とする。
    ↓
処理を中断する。
    ↓
RunWorkerCompletedイベントハンドラにて
e.Cancelledプロパティがtrueになっている。



進行状況の更新

進行状況の更新を報告できるように設定します。
bw.WorkerReportsProgress = true;


ProgressChangedイベントにフックします。
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);


ProgressChangedイベントにフックするイベントハンドラの例)
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
}
ProgressChangedイベントハンドラ内ではメインスレッドのコントロールにアクセス可能です。


進捗状況を更新します。
worker.ReportProgress(progressPercentage);
ReportProgressメソッドを実行するとProgressChangedイベントが発生します。
このメソッドの引数として渡した値がProgressChangedイベントにフックしたイベントハンドラでe.ProgressPercentageとして受け取ることができます。

他の引数を一つ渡すことが出来ます。
worker.ReportProgress(progressPercentage, "Working...");


Sample Code

public partial class Form1 : Form
{
    BackgroundWorker bw;

    public Form1()
    {
        InitializeComponent();
        progressBar1.Maximum = 100;

        bw = new BackgroundWorker();

        //BackgroundWorker が非同期のキャンセルをサポートしているかどうかを
        //示す値を取得または設定します。
        bw.WorkerSupportsCancellation = true;

        //プログレスバー関連
        //BackgroundWorker が進行状況の更新を報告できるかどうかを示す値を取得または設定します。
        bw.WorkerReportsProgress = true;

        //ReportProgress が呼び出されたときに発生します。
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

        //RunWorkerAsync が呼び出されたときに発生します。 
        //実際にバックグラウンド処理を行うイベントハンドラ
        //イベントにフックする。
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);

        //バックグラウンド操作の完了時、キャンセル時、
        //またはバックグラウンド操作によって例外が発生したときに発生します。
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
    }

    private void btnExec_Click(object sender, EventArgs e)
    {
        //IsBusy - BackgroundWorker が非同期操作を実行中かどうかを示す値を取得します。
        if (!bw.IsBusy)
        {
            //バックグラウンド操作の実行を開始します。
            bw.RunWorkerAsync(100);
        }
    }

    void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bw = sender as BackgroundWorker;

        int i = 0, count = (int)e.Argument;
        while (i < count)
        {
            Thread.Sleep(50);
            bw.ReportProgress(++i);
            if (bw.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
        }
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        string msg = string.Empty;

        if (e.Cancelled)
        {
            msg = "Cancelled";
        }
        else
        {
            msg = "Done";
        }

        MessageBox.Show(msg);

        progressBar1.Value = 0;
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        if (bw.IsBusy)
        {
            bw.CancelAsync();
        }
    }
}

Picasa ウェブ アルバムにアップロードするならPicasa3の「ウェブに同期」がラク

今現在、Picasaの最新のバージョンは3.1ですが、Picasa2.7を使用している場合、自動的にPicasa3には自動的にアップデートされないので、Picasaを起動してバージョン確認することをお勧めします。

Picasaを起動して、左側のフォルダツリーから同期させるフォルダを一つ選択します。
フォルダを選択すると以下のように同ウィンドウ右側に[ウェブに同期]ボタンが表示されます。

この[ウェブに同期]ボタンを押下すると、
同期するか確認するダイアログが表示されます。


「はい」を選択すると対象フォルダと同じ名前のウェブアルバムが作成されます。
後はこのフォルダにアップロードしたいファイルをブチ込んでおくだけでPicasaを起動したときに同期されます。


同期を無効にする

[▼]ボタンを押下し、[同期を無効にする]を選択すると同期を無効にすることができます。


Picasa → Picasa ウェブ アルバムに同期される変更
  • 写真の編集 (基本編集、調整、効果)
  • 追加または削除した写真
  • 説明、タグ、ジオタグに加えた変更
  • アルバムのプロパティ (名前、説明、場所)
  • 他のアプリケーションで行ってハード ドライブに保存した編集


同期されない変更
  • 写真の順序変更
  • ファイル名に対する変更
※[ウェブに同期] 機能で変更を同期できるのは、Picasa から Picasa ウェブ アルバムへのみです。


PYA

Picasa の基本
Picasa ウェブ アルバムとの変更の同期: [ウェブに同期] の基礎

別スレッドで処理を行うメリットはマルチCPUの恩恵みにある

比較に使用する処理
System.IO名前空間の「Directory.GetFiles」とSystem.Text.RegularExpressions名前空間の「Regex.IsMatch」を使用して特定のフォルダ以下にある全てのイメージファイルを取得する。
※当環境でファイル数が約50000弱,その内の約45000のファイルパスを取得する処理となる。

Sample Code

string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string[] files = Directory.GetFiles(
    folderPath, "*", SearchOption.AllDirectories);

List<string> imgList = new List<string>();

for (int i = 0; i < files.Length; i++)
{
    //正規表現でイメージファイルかを判断し格納。
    if (regex.IsMatch(files[i])) imgList.Add(files[i]);
}


比較分類

  1. メインスレッド(シングルスレッド。通常の処理)で行う場合
  2. BackgroundWorkerを使用して別スレッドで行う場合

上記の処理を共に10回行い、処理に掛かる時間を比較する。

環境
  • OS:Windows Vista
  • CPU: Core2 Duo 2.66GHz
  • RAM: 2GB


1回の処理が終了するまで次の処理を行わない条件で計測する

1回の処理に掛かる時間の平均
  • メインスレッド:1225.477ms
  • 別スレッド:1236.175ms

結論
その差は約0.01秒。
相対的に比較して、この程度の処理で出される差は誤差と言える程度のものでしかなかった。


1回の処理が終了したかに関わらず、処理を行った場合

Sample Code(BackgroundWorkerを使用した処理のみ)
class Program
{
    static void Main(string[] args)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);

        for (int i = 0; i < 10; i++)
        {
            if (!bw.IsBusy)
            {
                bw.RunWorkerAsync();
            }
            else
            {
                NewThread();
            }
        }
    }

    static void NewThread()
    {
        BackgroundWorker nbw = new BackgroundWorker();
        nbw.DoWork += new DoWorkEventHandler(bw_DoWork);
        nbw.RunWorkerAsync();
    }

    static void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        string[] files = Directory.GetFiles(
            Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
            "*",
            SearchOption.AllDirectories
            );

        List<string> imgList = new List<string>();
        Regex regex = new Regex(@"\.(BMP|DIB|RLE|JPG|JPEG|JPE|JFIF|GIF|EMF|WMF|TIF|TIFF|PNG|ICO)$");

        for (int i = 0; i < files.Length; i++)
        {
            if (regex.IsMatch(files[i])) imgList.Add(files[i]);
        }

        e.Result = imgList.ToArray();
    }
}

10回処理を行ったトータルの処理時間

  • メインスレッド:11977.108ms
  • 別スレッド:6786.517ms


メインスレッドでループを回した処理に掛かった時間は1回の処理時間×ループ回数とほぼ一致するが、別スレッドで行われた処理では明らかに処理に掛かった時間が短縮された。

タスクマネージャでCPU使用率を見るとデュアルコアCPU環境では、シングルスレッドで処理を行わせた場合、片方のCPUしか使用されていないが、別スレッドで次々に処理を行わせた場合ではCPUをフルに使用して処理を行っていることを確認できる。
このため、別スレッドで次々に処理を行う方が処理に掛かる時間が短くなったと言える。

ナノ秒という単位の精度で経過時間を計る - C#

ナノ秒という単位の精度で経過時間を取得するために使用するのが
QueryPerformanceCounterQueryPerformanceFrequencyです。

Sample

using System;
using System.Runtime.InteropServices;

public class TraceTime
{
    #region
    [DllImport("kernel32.dll")]
    extern static int QueryPerformanceCounter(ref long x);

    [DllImport("kernel32.dll")]
    extern static int QueryPerformanceFrequency(ref long x);
    #endregion

    #region フィールド
    long st;
    long end;
    long frq;
    #endregion

    public TraceTime()
    {
        st = 0;
        end = 0;
        QueryPerformanceFrequency(ref frq);
    }

    public void Start()
    {
        QueryPerformanceCounter(ref st);
    }

    public void Stop()
    {
        QueryPerformanceCounter(ref end);
    }

    public double NowResult()
    {
        long now = 0;
        QueryPerformanceCounter(ref now);
        return (double)(now - st) / frq;
    }

    public double Result()
    {
        return (double)(end - st) / frq;
    }
}

Test

1秒Sleepさせて経過時間を取得する処理を10回繰り返しています。
class Program
{
    static void Main(string[] args)
    {
        TraceTime traceTime = new TraceTime();
        int loop = 10;
        double[] ttm = new double[loop];

        for (int i = 0; i < loop; i++)
        {
            traceTime.Start();
            System.Threading.Thread.Sleep(1000);
            ttm[i] = traceTime.NowResult();
        }

        Console.WriteLine("Average:" + ttm.Average());
        Console.WriteLine("Min:" + ttm.Min());
        Console.WriteLine("Max:" + ttm.Max());
        Console.WriteLine("Error Range:" + (ttm.Max() - ttm.Min()));

        Console.ReadLine();
    }
}

Result

Average:1.00004832318074
Min:0.999835104740966
Max:1.0001991174856
Error Range:0.00036012744636488

重要なのは以下の部分だけです。
[DllImport("kernel32.dll")]
extern static int QueryPerformanceCounter(ref long x);

[DllImport("kernel32.dll")]
extern static int QueryPerformanceFrequency(ref long x);
後は使いやすいように追加/修正するだけです。
と言っても、値のチェックを入れるぐらいで他は大体同じになるのですが。

FYA

以下のサイトには例外のthrowまで書かれたものがありましたので参考に。
わんくまライブラリ Wankuma.Interop.InteropQueryPerformanceCounterクラス Version2

演算子(operator)をイジるのイイ感じ - C#

「演算子をオーバーロード」などとMSDNにあるのですが、「operator string」がイイ感じなんで機会があればぜひ。

その前にToStringをoverrideしておくと更に分かりやすいので以下のようにしました。

public override string ToString()
{
    return this.Age.ToString();
}

stringとの比較に使用される値を定義します。
public static implicit operator string(Datum x)
{
    return x.ToString();
}

これはd3というオブジェクトと文字列"1"を比較しています。
Console.WriteLine("string. {0}",d3 == "1");
「d3.Age」の値が「1」の場合、trueが返ります。

キャストや「Convert.ToInt32」などとしなくとも比較できるようになります。
例えば、
if (datum == textBox1.Text)
{
    //...
}
などということも可能になります。


更にintで比較する場合も定義してみました。
public static implicit operator int(Datum x)
{
    return x.Age;
}

Console.WriteLine("int. {0}", d3 == 1);
「d3.Age」の値が「1」の場合、trueが返ります。


その他、実際に試して、「+」 や「++」をoverroadした際の挙動を確認し、正しく動作する書き方は以下の通りとなりました。

Sample Code

using System;

namespace ConsoleApplication1
{
    class Datum
    {
        #region フィールド
        int _age;
        string _name;
        #endregion

        #region コンストラクタ
        public Datum()
        {
            _age = 0;
            _name = string.Empty;
        }

        public Datum(int age)
        {
            _age = age;
            _name = string.Empty;
        }

        public Datum(int age, string name)
        {
            _age = age;
            _name = name;
        }
        #endregion


        #region プロパティ
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }
        #endregion


        public static implicit operator string(Datum x)
        {
            return x.ToString();
        }

        public static implicit operator int(Datum x)
        {
            return x.Age;
        }


        public static Datum operator +(Datum datum1, Datum datum2)
        {
            //これは×
            //return new Datum(datum1.Age + datum2.Age, datum1.Name);

            Datum tmp = new Datum(datum1.Age + datum2.Age, datum1.Name);
            //or Datum tmp = new Datum(datum1.Age + datum2.Age, datum1.Name + datum2.Name);
            return tmp;
        }

        public static Datum operator ++(Datum datum)
        {
            //これは×
            //datum.Age++;
            //return datum;

            //これは×
            //datum.Age = datum.Age + 1;
            //return datum;

            //これは一応○
            //Datum tmp = new Datum(datum.Age + 1, datum.Name);
            //return tmp;

            return new Datum(datum.Age + 1, datum.Name);
        }

        public override string ToString()
        {
            return this.Age.ToString();
        }
    }
}

Test

class Program
{
    static void Main(string[] args)
    {
        Datum d1 = new Datum();
        d1.Age = 1;
        Datum t1 = d1++;
        Console.WriteLine("d1 = {0}", d1);
        Console.WriteLine("t1 = {0}", t1);
        

        Datum d2 = new Datum();
        d2.Age = 1;
        Datum t2 = ++d2;
        Console.WriteLine("d2 = {0}", d2);
        Console.WriteLine("t2 = {0}", t2);


        Datum d3 = new Datum(1,string.Empty);
        Console.WriteLine("string. {0}",d3 == "1");
        Console.WriteLine("int. {0}", d3 == 1);

        Console.ReadLine();
    }
}

Result

d1 = 2
t1 = 1
d2 = 2
t2 = 2
string. True
int. True

イベントハンドラを取得する - C#

イベントにフックされているイベントハンドラを取得する方法

例えば、フォームにbutton1というボタンがあり、button1のクリックイベントにフックされているイベントハンドラを取得して、何らかの処理を行いたい場合があります。
そのようなことを行うにはSystem.Reflectionを使用します。
using System;
using System.ComponentModel;
using System.Reflection;

namespace WindowsFormsApplication1
{
    public class EventDatum
    {
        public static Delegate GetEventHandler(object obj, string eventName)
        {
            EventHandlerList ehl = GetEvents(obj);
            object key = GetEventKey(obj, eventName);
            return ehl[key];
        }



        private delegate MethodInfo delGetEventsMethod(Type objType, delGetEventsMethod callback);
        private static EventHandlerList GetEvents(object obj)
        {
            delGetEventsMethod GetEventsMethod = delegate(Type objtype, delGetEventsMethod callback)
            {
                MethodInfo mi = objtype.GetMethod("get_Events", All);
                if ((mi == null) & (objtype.BaseType != null))
                    mi = callback(objtype.BaseType, callback);
                return mi;
            };

            MethodInfo methodInfo = GetEventsMethod(obj.GetType(), GetEventsMethod);
            if (methodInfo == null) return null;
            return (EventHandlerList)methodInfo.Invoke(obj, new object[] { });
        }



        private delegate FieldInfo delGetKeyField(Type objType, string eventName, delGetKeyField callback);
        private static object GetEventKey(object obj, string eventName)
        {
            delGetKeyField GetKeyField = delegate(Type objtype, string eventname, delGetKeyField callback)
            {
                FieldInfo fi = objtype.GetField("Event" + eventName, All);
                if ((fi == null) & (objtype.BaseType != null))
                    fi = callback(objtype.BaseType, eventName, callback);
                return fi;
            };

            FieldInfo fieldInfo = GetKeyField(obj.GetType(), eventName, GetKeyField);
            if (fieldInfo == null) return null;
            return fieldInfo.GetValue(obj);
        }



        private static BindingFlags All
        {
            get
            {
                return
                    BindingFlags.Public |
                    BindingFlags.NonPublic |
                    BindingFlags.Instance |
                    BindingFlags.IgnoreCase |
                    BindingFlags.Static;
            }
        }
    }
}

なぜget_Eventsなのか

取得したいのはEventHandlerList型の変数名がEventsというオブジェクトなのですが、何故get_Eventsというメソッドを取得しているのでしょうか?
ILDASM.exeでILコード見てみると以下の一行を見ることができます。
IL_0001:  call       instance class [System]System.ComponentModel.EventHandlerList [System]System.ComponentModel.Component::get_Events()
つまり、ILコードでは「get_ + 元変数名」という名のメソッドになっているからです。

Keyの変数名は「"Event" + イベントネーム」

.Net Frameworkにある既存のコンポーネントに使用されているキー名の多くがこの形となります。
詳しくは「イベントハンドラの取得に使われるキー名の一覧 - C#」を参照下さい。

その他のポイント

GetEventsメソッド
return (EventHandlerList)methodInfo.Invoke(obj, new object[] { });
コードを見るとGetType()とあるように型からメソッドを取得しています。しかし、中身はインスタンスにあります。ですから、MethodInfoのInvokeメソッドを使用して現在のインスタンスによって表されるメソッドまたはコンストラクタを呼び出しています。

GetEventKeyメソッド
return fieldInfo.GetValue(obj);
指定したオブジェクトのフィールドの値を返しています。

使用例

以下の例ではbutton2をクリックするとbutton1のクリックイベントにフックされたイベントハンドラを呼び出しています。
private void button1_Click(object sender, EventArgs e)
{
    try
    {
        Button btn = sender as Button;
        MessageBox.Show(btn.Name + " Clicked.");
    }
    catch
    {
        MessageBox.Show("sender Is Not Button.");
    }
}

private void button2_Click(object sender, EventArgs e)
{
    Delegate handler = EventDatum.GetEventHandler(button1, "Click");

    if (handler != null)
    {
        handler.DynamicInvoke(new object[] { sender, e });
    }
}

参考:
http://www.technewsgroups.net/group/microsoft.public.dotnet.general/topic4196.aspx

イベント・基本パターン - C#

イベントの基本的な作成例を書いてみます。

まずはコードから。

using System;
using System.ComponentModel;

namespace EventSample
{
    class Fuga
    {
        #region Event
        //using System.ComponentModel;が必要
        protected EventHandlerList Events = new EventHandlerList();

        private static readonly object EventValueChanged = new object();

        public delegate void ValueChangedEventHandler(object sender, EventArgs e);

        public event ValueChangedEventHandler ValueChanged
        {
            add { Events.AddHandler(EventValueChanged, value); }
            remove { Events.RemoveHandler(EventValueChanged, value); }
        }

        protected void RaiseEvent(object key, EventArgs e)
        {
            Delegate handler = Events[key];

            if (handler != null)
            {
                handler.DynamicInvoke(new object[] { this, e });
            }
        }

        protected virtual void OnValueChanged(EventArgs e)
        {
            RaiseEvent(EventValueChanged, e);
        }
        #endregion


        #region Constructor
        public Fuga()
        {
            _value = 0;
        }
        #endregion


        #region Property
        int _value;
        public int Value
        {
            get
            {
                return _value;
            }
            set
            {
                if (this.Value != value)
                {
                    _value = value;
                    this.OnValueChanged(new EventArgs());
                }
            }
        }
        #endregion

    }


    class Program
    {
        static void Main(string[] args)
        {
            Fuga fuga = new Fuga();

            //フックする
            fuga.ValueChanged +=new Fuga.ValueChangedEventHandler(fuga_ValueChanged);

            fuga.Value = 1;

            //or ValueChangedイベントを意図的に発生させるなら
            //以下をコメントアウトして下さい。
            //fuga.OnValueChanged(new EventArgs());
        }

        //イベントが発生すると行われるメソッド
        private static void fuga_ValueChanged(object sender, EventArgs e)
        {
            Console.WriteLine("Value Changed.");
        }
    }
}


イベントハンドラの入れ物 - Events

protected EventHandlerList Events = new EventHandlerList();
『EventHandlerList』と言う名のとおり、イベントハンドラをこの"Events"に格納します。
System.ComponentModelを継承しているオブジェクトであれば、既にあるはずなので作成する必要はありません。
例えば、新規Windowsアプリケーションを作成し、デフォルトで作成されるフォームのソースに"Events"と入力すればインテリセンス機能で以下のように表示されます。
これはフォームがSystem.ComponentModelを継承しているからです。


イベントハンドラを取り出すには以下のようにします。
Delegate handler = Events[key];
handlerに格納したイベントハンドラはマルチキャストデリゲートですので、対象のイベントに複数イベントハンドラがフックされていてもDelegate[]とはなりません。
このように、Eventsからイベントハンドラを取り出すには一意であるキーとなるオブジェクトをインデックスに指定する必要があります。

キーの作成

キーの作成例)
private static readonly object EventValueChanged = new object();
このキーそのものに何モノかを代入したりして使用することはありません。
既存のコンポーネントの場合、キーの命名にはある程度の規則性があります。
Event + イベント名
独自に作成する場合にも、Reflectionを使用してこのイベントハンドラを取得する場合を考慮すると、その規則に出来るだけ合わせることが望ましいのでは、と考えます。

イベントハンドラの型を定義する

イベントハンドラの型がそのままイベントの型になります。つまり対象のイベントにフックできるメソッドの型をイベント宣言時にイベントの型として使用します。
public delegate void ValueChangedEventHandler(object sender, EventArgs e);
しかし、不必要に型を定義しなくとも通常はSystem.EventHandlerを使用すれば良いでしょう。
例) public event EventHandler ValueChanged

イベントのアクセサ

イベントもアクセサを作成することで実際の内部での出し入れを制御します。
このアクセサを通してEventsへの出し入れを行います。
public event ValueChangedEventHandler ValueChanged
{
    add { Events.AddHandler(EventValueChanged, value); }
    remove { Events.RemoveHandler(EventValueChanged, value); }
}

RaiseEventメソッドの作成

このメソッドの作成は効率化だけでなく、統一化も含まれています。
protected void RaiseEvent(object key, EventArgs e)
{
    Delegate handler = Events[key];

        if (handler != null)
        {
            handler.DynamicInvoke(new object[] { this, e });
        }
}
引数のEventArgsの型やsenderの指定など、イベントの種類によって作成し、使い分けるのが良いでしょう。

例えば、フォームのRaiseEventメソッドには以下のものがあります。
protected void RaiseDragEvent(object key, DragEventArgs e);
protected void RaiseKeyEvent(object key, KeyEventArgs e);
protected void RaiseMouseEvent(object key, MouseEventArgs e);
protected void RaisePaintEvent(object key, PaintEventArgs e);

イベントを発生させるメソッドの作成

イベントによっては意図的にそのイベント発生させることが必要な場合があります。そのためのメソッドを作成します。アクセス修飾子はもちろんpublicです。
public virtual void OnValueChanged(EventArgs e)
{
    RaiseEvent(EventValueChanged, e);
}
一般的にメソッド名は「On + イベント名」とします。

このメソッドの用途はこれだけではありません。
上記のコードで実際にこのメソッドが使われている箇所を見てみます。
public int Value
{
     get
    {
        return _value;
    }
    set
    {
        if (this.Value != value)
        {
             this._value = value;
            this.OnValueChanged(new EventArgs());
        }
    }
}

もし、効率を考えるなら以下のようにするのが妥当です。
set
{
    if (this.Value != value)
    {
        this._value = value;
        if (Events[EventValueChanged] != null)
        {
            Events[EventValueChanged].DynamicInvoke(new object[] { this, new EventArgs() });
        }
    }
}
ワザワザ回りくどくイベントハンドラを呼び出しているのは、理由があります。
もし、継承したクラスでValueChangedイベントを発生させたくない場合、以下のようにすることでイベントの発生を防ぐことができます。
class FugaFuga : Fuga
{
    protected override void OnValueChanged(EventArgs e) { }
}
上記はFugaFugaクラスがFugaクラスを継承し、OnValueChangedメソッドをオーバーライドしています。
メソッド内に敢えて何も記述しないことでValueChangedイベントが発生しても何も起こらなくしています。
このようなことを可能にするために、イベントを起こすメソッドにはvirtualを指定します。
public virtual void OnValueChanged(EventArgs e)

イベントハンドラの取得に使われるキー名の一覧 - C#

ある特定のイベントのイベントハンドラを取得するにはイベントハンドラを格納しているEvents(EventHandlerList型)のインデックスに対象イベントのキーとなるオブジェクトを渡す必要がある。

例えば以下のように取得する。

Delegate handler = Events[key];

Reflectionを使用してイベントハンドラを取得するにはこのキーであるオブジェクトの名前を知る必要がある。
個人的に調べた結果を以下に示すが、ここにないものも、命名には一定の規則が存在するので以下を参考に探ると良いでしょう。

全般

イベント名 キー名
Click EventClick
TextChanged EventText
FontChanged EventFont
ForeColorChanged EventForeColor
KeyPress EventKeyPress
Resize EventResize
SizeChanged EventSize
Paint EventPaint
MouseDown EventMouseDown

Form

Load EVENT_LOAD
ResizeBegin EVENT_RESIZEBEGIN
FormClosed EVENT_FORMCLOSED
FormClosing EVENT_FORMCLOSING

ToolStripMenuItem

CheckedChanged EventCheckedChanged
CheckStateChanged EventCheckStateChanged

CheckBox

CheckedChanged EVENT_CHECKEDCHANGED
CheckStateChanged EVENT_CHECKSTATECHANGED

ListBox

SelectedIndexChanged EVENT_SELECTEDINDEXCHANGED
SelectedValueChanged EVENT_SELECTEDVALUECHANGED

PictureBox

StyleChanged EventStyleChanged

ComboBox

TextUpdate EVENT_TEXTUPDATE

Sony Style(ソニースタイル)
デル株式会社

Recent Posts