【C#】初めてのイベント【WPF】

# 本記事の概要

　本記事ではC#学習を始めたばかりの方を対象としています。初心者が理解しにくい部分などは理屈抜きで実現する方法を書いていきます。まずは基本的な動きから理解していきましょう。

# そもそもイベントって？
 C#のイベント機能は、あるクラスで発生した出来事を，あらかじめ登録された一群のメソッドに対して、1回の呼び出しによって、ですべて伝える機能です。

イメージ画像
![イベント.png](https://qiita-image-store.s3.amazonaws.com/0/183354/f0ecb91e-7df6-b9d2-e4e6-376c146e8fe9.png)


発生側が発火（イベントの実行）を行うことにより、登録していたイベント受け取り側が実行されます。

# 実際にイベントを書いてみる

## SleepClassの作成
イベントが発生するようにしてみる。このときイベントが発生するだけで何もデータを返さないものとする

```csharp:SleepClass.cs

public class SleepClass
{
    // データを持たないイベントデリゲートの宣言
    //ここでは"Time"というイベントデリゲートを宣言する
    public event EventHandler Time;

    public void Start()
    {
        System.Threading.Thread.Sleep(5000);
        if (Time != null)
        {
            //"Time"イベントの発生
            Time(this, EventArgs.Empty);
        }
    }
}
```
## SampleClassの作成
イベントを受け取る側では次のように宣言する。ここではbutton_ClickイベントによりSleepClassクラスのStartメソッドを呼び出し、イベント「Time」が発生するとSleepClass_Timeが呼び出され実行される。


```csharp:SampleClass.cs
private void button1_Click(object sender, System.EventArgs e)
{
    SleepClass clsSleep = new SleepClass();
    //イベントハンドラの追加
    clsSleep.Time += new EventHandler(this.SleepClass_Time);
    clsSleep.Start();
}

private void SleepClass_Time(object sender, System.EventArgs e)
{
    //イベントが発生したとき
    MessageBox.Show("OK!");
}
```


ここでEventHandlerやEventArgsとは一体なんだろうという疑問だと思いますが、この記事では開設は省きますので、別途調べてください。
イベントハンドラを追加している部分で別のメソッドを追加すれば、むろんそちらも登録されます。

## SleepClassの改良
これでほとんどの場合用が済んでしまうような気もするが、SleepClassを少し改良してみます。

```csharp:SleepClass.cs

public class SleepClass
{
    // データを持たないイベントデリゲートの宣言
    public event EventHandler Time;

    protected virtual void OnTime(EventArgs e)
    {
        if (Time != null)
        {
            Time(this, e);
        }
    }

    public void Start()
    {
        System.Threading.Thread.Sleep(5000);
        OnTime(EventArgs.Empty);
    }
}
```

何が変わったかはわかるとコードを見ていただければわかると思います。
protected virtual void OnTime(EventArgs e)
が増えてますね。このSleepClassを継承して新たなクラスを作ることを考えればこうするべきです。
今回は特にデータを返さないため簡単な処理に終わっています。では次にデータを返すイベントを作成してみましょう。


# データを返すイベントの作成

次はデータを返すイベントを作成する方法を考えましょう。

返されるデータは上の例の「EventArgs」から継承されるクラスになるため、結局その部分だけを書き換えることになります。

```csharp:Sleep.cs
//Timeイベントで返されるデータ
//ここではstring型のひとつのデータのみ返すものとする
public class TimeEventArgs : EventArgs
{
    public string Message;
}

public class SleepClass
{
    //デリゲートの宣言
    //TimeEventArgs型のオブジェクトを返すようにする
    public delegate void TimeEventHandler(object sender, TimeEventArgs e);
    
    //イベントデリゲートの宣言
    public event TimeEventHandler Time;
    
    protected virtual void OnTime(TimeEventArgs e)
    {
        if (Time != null)
        {
            Time(this, e);
        }
    }
    
    public void Start()
    {
        System.Threading.Thread.Sleep(5000);
        //返すデータの設定
        TimeEventArgs e = new TimeEventArgs();
        e.Message = "終わったよ。";
        //イベントの発生
        OnTime(e);
    }
```

```csharp:SampleClass.cs
private void button1_Click(object sender, System.EventArgs e)
{
    SleepClass clsSleep = new SleepClass();
    clsSleep.Time += new SleepClass.TimeEventHandler(this.SleepClass_Time);
    clsSleep.Start();
}

private void SleepClass_Time(object sender, TimeEventArgs e)
{
    //返されたデータを取得し表示
    MessageBox.Show(e.Message);
}
```


 返すデータを入れるためのクラス、今回でいうところのTimeEventArgsが必要となってくる。
勿論イベントの引数として追加してやっても実行はできるが、コードの修正や使いまわしを考えるとクラスを１つ作り、EventArgsを渡したほうが効率的だと思います。

 さらにsender(呼び出し元のクラス)も別段渡す必要はありませんが、MSの考え的にはこうして書いてほしいらしいので、このように書きましょう。

# 終わりに

　この記事を読んで少しでもイベントを理解できたら幸いです。
また、詳しく知りたい方は[言語に内蔵されたイベント機能](http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_013/csharp_abc01.html)こちらを一読することをお勧めします。

# 参考

- [C#のイベント機能](http://dobon.net/vb/dotnet/vb2cs/event.html)