C#ならボタンがクリックされたとかキーが押されたなどのイベントが発生したらどんな処理をするのかという感じでプログラミングしていきます。イベントは既存のものだけでなく自分で定義することもできます。
例としてメインのフォームとは別にもうひとつフォームを生成します。そしてふたつめのフォームでマウスボタンが押下されたらそれをメインのフォームに伝えて、クリックした旨を表示させます。
それだけなら自分でイベントを実装する必要はありません。これで充分なはずです。
Form1のボタンをクリックしたらForm2が出現します。そしてForm2のクライアント領域をクリックするとクリックされた座標がコンソールに表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { Form2 _form2 = new Form2(); public Form1() { InitializeComponent(); _form2.MouseDown += _form2_MouseDown; } private void button1_Click(object sender, EventArgs e) { _form2.ShowDialog(); } private void _form2_MouseDown(object sender, MouseEventArgs e) { Console.WriteLine($"Form2.MouseDownイベント:クリックされた座標は({e.X},{e.Y})"); } } |
Contents
イベントを自分で定義する
ただ、ここでは自分でイベントを実装したいので、あえて面倒な方法をとります。いわばサンプルプログラムです。
Form2クラス側でクライアント領域がクリックされたことを、自分で実装したイベントでForm1クラスに伝えるだけならこれで充分です。
ただこれではForm2のどこがクリックされたのかを伝えることができません。ただクリックされたことを伝えることができるだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public partial class Form2 : Form { public event EventHandler MyEvent1; protected override void OnMouseDown(MouseEventArgs e) { MyEvent1?.Invoke(this, new EventArgs()); base.OnMouseDown(e); } } public partial class Form1 : Form { Form2 _form2 = new Form2(); public Form1() { InitializeComponent(); _form2.MyEvent1 += _form2_MyEvent1; } private void _form2_MyEvent1(object sender, EventArgs e) { Console.WriteLine($"Form2.MyEvent1イベント:クリックされた"); } } |
イベントハンドラで値を取得できるようにする
そこでクリックされた座標もイベントハンドラで取得できるようにします。
まずEventArgsを継承してMyEventArgsクラスを定義します。コンストラクタに座標を渡せばどこがクリックされたのかをForm1クラスに伝えることができます。
MyEvent2?.Invokeの第二引数にEventArgsのインスタンスではなくMyEventArgsのインスタンスを渡しているのでイベントハンドラでMyEventArgsにキャストしなければクリックされた座標を取得することができません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public class MyEventArgs : EventArgs { public MyEventArgs(Point point) { Point = point; } public Point Point { get; } } public partial class Form2 : Form { public event EventHandler MyEvent2; protected override void OnMouseDown(MouseEventArgs e) { Point point = new Point(e.X, e.Y); MyEventArgs args = new MyEventArgs(point); MyEvent2?.Invoke(this, args); base.OnMouseDown(e); } } public partial class Form1 : Form { Form2 _form2 = new Form2(); public Form1() { InitializeComponent(); _form2.MyEvent2 += _form2_MyEvent2; } private void _form2_MyEvent2(object sender, EventArgs e) { MyEventArgs args = (MyEventArgs)e; Console.WriteLine($"Form2.MyEvent2イベント:クリックされた座標は({args.Point.X},{args.Point.Y})"); } } |
デリゲートを宣言してイベントハンドラで自由にデータを取得できるようにする
イベントを送信するときMyEvent?.Invoke(object sender, EventArgs e);のような形にするのが一般的ですが、第二引数はEventArgs型でなければならないのかというとそうではありません。好きな型を渡すことができます。ただし、この場合はイベントを定義するときに以下のような処理をいれる必要があります。
1 2 |
public delegate void MyEvent3Handler(object sender, MyEventArgs e); public event MyEvent3Handler MyEvent3; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public partial class Form2 : Form { public delegate void MyEvent3Handler(object sender, MyEventArgs e); public event MyEvent3Handler MyEvent3; protected override void OnMouseDown(MouseEventArgs e) { Point point = new Point(e.X, e.Y); MyEventArgs args = new MyEventArgs(point); MyEvent3?.Invoke(this, args); } } public partial class Form1 : Form { Form2 _form2 = new Form2(); public Form1() { InitializeComponent(); _form2.MyEvent3 += _form2_MyEvent3; } private void _form2_MyEvent3(object sender, MyEventArgs e) { Console.WriteLine($"Form2.MyEvent3イベント:クリックされた座標は({e.Point.X},{e.Point.Y})"); } } |
イベントハンドラの引数は自由に設定できる
さらに言ってしまうとMyEvent?.Invoke(object sender, EventArgsまたはそれを継承したクラス e);というような引数の型に縛りがあるわけでもありません。こんなイベントを実装することもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public partial class Form2 : Form { public delegate void MyEvent4Handler(int x, int y); public event MyEvent4Handler MyEvent4; protected override void OnMouseDown(MouseEventArgs e) { MyEvent4?.Invoke(e.X, e.Y); base.OnMouseDown(e); } } public partial class Form1 : Form { Form2 _form2 = new Form2(); public Form1() { InitializeComponent(); _form2.MyEvent4 += _form2_MyEvent4; } private void _form2_MyEvent4(int x, int y) { Console.WriteLine($"Form2.MyEvent4イベント:クリックされた座標は({x},{y})"); } } |
MyEvent?.Invoke();の引数ですが、2個でなければならないという縛りもありません。こんなのもありです。
下記のコードならクリックされた座標だけでなくクリックされた時刻や回数も取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public partial class Form2 : Form { public Form2() { InitializeComponent(); } int _count = 0; public delegate void MyEvent5Handler(int x, int y, DateTime time, int count); public event MyEvent5Handler MyEvent5; protected override void OnMouseDown(MouseEventArgs e) { _count++; MyEvent5?.Invoke(e.X, e.Y, DateTime.Now, _count); base.OnMouseDown(e); } } public partial class Form1 : Form { Form2 _form2 = new Form2(); public Form1() { InitializeComponent(); _form2.MyEvent5 += _form2_MyEvent5; } private void _form2_MyEvent5(int x, int y, DateTime time, int count) { string s1 = $"クリックされた座標は({x},{y})"; string s2 = $"時刻は{time.ToLongTimeString()}"; string s3 = $"{count}回目のクリックです"; Console.WriteLine($"Form2.MyEvent5イベント:{s1}。{s2}。{s3}。"); } } |
delegateは何回見ても覚えられなかったのですが、
今回は、大変勉強になりました。