プログラム言語 イベント、委譲

Delegate

基本

delegate = 処理を格納する変数
関数ポインタ(C++)

static class MyClass
{
 public static void MyFunc1() { }
 public static void MyFunc2(int prm) { }
 public static int MyFunc3() { return 100; }
 public static int MyFunc4(int prm) { return prm; }
}

class Program
{
 デリゲート宣言
 delegate void MyDelegator1();
 delegate void MyDelegator2(int prm);
 delegate int MyDelegator3();
 delegate int MyDelegator4(int prm);  

 static void Main(string[] args)
 {
  デリゲート作成
  MyDelegator1 m1 = new MyDelegator1(MyClass.MyFunc1);
  MyDelegator2 m2 = new MyDelegator2(MyClass.MyFunc2);
  MyDelegator3 m3 = new MyDelegator3(MyClass.MyFunc3);
  MyDelegator4 m4 = new MyDelegator4(MyClass.MyFunc4);
  
  デリゲート作成(↓も可)
  MyDelegator1 m1 = MyClass.MyFunc1;
  MyDelegator2 m2 = MyClass.MyFunc2;
  MyDelegator3 m3 = MyClass.MyFunc3;
  MyDelegator4 m4 = MyClass.MyFunc4;

  
  実行
  m1();
  m2(prm: 5);
  int ret = m3();
  int ret = m4(prm: 5);
 }
}

Private Delegate Sub MrDelegate()

Private Class clsDelegate
 Public Sub Delegated()
  ’ここが呼ばれる
 End Sub
End Class

Private Sub myForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load

 Dim myDelegate As New clsDelegate
 Dim mrDelegate As MrDelegate = AddressOf myDelegate.Delegated

 Call mrDelegate.Invoke()

End Sub

マルチキャスト

static class MyClass
{
 public static void MyFunc1() { }
 public static void MyFunc2() { }
 public static void MyFunc3() { }
}

class Program
{
 delegate void MyDelegator();

 static void Main(string[] args)
 {
  MyDelegator m = MyClass.MyFunc1;
  
  マルチキャスト
  m -= MyClass.MyFunc1;
  m += MyClass.MyFunc2;
  m += MyClass.MyFunc3;
  
  実行
  m();
 }
}

匿名メソッド

delegate void MyDelegator1();
delegate int MyDelegator2(int prm);

static void Main(string[] args)
{
 匿名メソッド
 MyDelegator1 m1 = delegate
 {
  var ary = Enumerable.Range(start:1, count:10);
  foreach (var elm in ary) Console.WriteLine(elm);
 };
 
 匿名メソッド(引数/戻り値あり)
 MyDelegator2 m2 = delegate(int prm)
 {
  return prm;
 };
 
 m1();
 int ret = m2(prm: 6);
}

delegate void MyDelegator1();
delegate int MyDelegator2(int prm);

static void Main(string[] args)
{
 匿名メソッド
 MyDelegator1 m1 = () =>
 {
  var ary = Enumerable.Range(start:1, count:10);
  foreach (var elm in ary) Console.WriteLine(elm);
 };
 
 匿名メソッド(引数/戻り値あり)
 MyDelegator2 m2 = (prm) =>
 {
  return prm;
 };
 
 m1();
 int ret = m2(prm: 6);
}

匿名メソッド(LINQ)

var ary = Enumerable.Range(start: 1, count: 10);
var query = ary.Where(delegate(int prm) {
 return prm > 5;
}
);
foreach (var elm in query) Console.WriteLine(elm);
→6 7 8 9 10

var ary = Enumerable.Range(start: 1, count: 10);
var query = ary.Where(n => n > 5);
foreach (var elm in query) Console.WriteLine(elm);
→6 7 8 9 10

イベント

規定イベント

//イベントを作成
private event EventHandler myEvent;
EventHandler : ~(object sender, EventArgs e)の引数が既に設定されているボタンクリック等用のイベント

private void myForm_Load(object sender, EventArgs e)
{
  //イベントを関連付け(1)
  myEvent += new EventHandler(this.CatchEvent);
  
  //new EventHanderを省略して↓ でも可
  myEvent += this.CatchEvent;
  
  //匿名メソッド↓ による記述も可
  //(記述済みのサブルーチンを指定せず、イベント登録と同時に処理内容を書く)
  myEvent += delegate
  {
    処理内容(2)
  };
  
  //イベントを発生させる
  myEvent(this, new EventArgs());
  ⇒(1)、(2)が実行される
}

private void CatchEvent(object sender, EventArgs e)
{
  //ここが呼ばれる
}

‘イベントを作成
Private Event myEvent()

Private Sub myForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load

  ’イベントとサブルーチンを関連付ける
  AddHandler myEvent, AddressOf CatchEvent
  
  ’イベントを発生させる
  RaiseEvent myEvent()
  
End Sub

Private Sub CatchEvent()
  ’このサブルーチンが呼ばれる
End Sub

画面作成

import java.awt.*;

public class myjava
{
 public static void main (String[] args)
 {

  Frame f = new Frame();
  f.setLayout(null);
  
  Label l = new Label("Hello");
  l.setBounds(20,40,100,20);
   x座標、y座標, 横幅, 縦幅
   f.add(l,BorderLayout.CENTER);
  
  Button b = new Button("OK");
  b.setBounds(20,100,100,20);
  f.add(b);
  
  TextField t = new TextField();
  t.setBounds(20,150,100,20);
  f.add(t);
  
  f.setSize(200,200);
  f.setVisible(true);
 }
}

イベント定義

import java.awt.*;
import java.awt.event.*;

public class myjava
{
 public static void main (String[] args)
 {

  Frame f = new Frame();
  f.setLayout(null);

  
  Button b = new Button("OK");
  b.setBounds(20,100,100,20);
  b.addActionListener(new MyButtonAction());
  f.add(b);
  
  f.setSize(200,200);
  f.setVisible(true);

 }
}

class MyButtonAction implements ActionListener {

 メソッド名はActionListenerインターフェイス定義により固定
 public void actionPerformed(ActionEvent e){
  System.out.println("クリックしました!");
 }
}

イベント一覧
ActionListener
ボタンクリック
テキストフィールドでEnterキー押下
メニューアイテム選択

WindowListener
フレーム閉じる

MouseListener
マウス押下

MouseMotionListener
マウスを動かす

FocusListener
フォーカス

ListSelectionListener
テーブル、リストの選択変更


<button id="myButton">button</button>

イベントハンドラー
~.onClick = ~
onClickプロパティを上書き
同様に設定したイベントプロパティを別イベントプロパティで上書きされる危険がある。


<script type="text/javascript">
  document.getElementById("myButton").onclick = function(){
    window.alert("Hello World");
  };
</script>

イベントリスナー
addEventListener(イベント, 関数名1, false)
addEventListener(イベント, 関数名2, false)
addEventListener(イベント, 関数名3, false)
複数の関数を登録でき、上書きされる危険は無い

var BrowserType;
if (window.addEventListener){
  BrowserType = "All";
}else if (window.attachEvent){
  BrowserType = "IE";
}else{
  BrowserType = "Other";
}

var myFunc = function(e){
 イベントリスナーが発生したタグ名を取得
 var myTag = e.target.tagName;
 
 イベントフローを中断(デフォルトアクションは実行される)
 e.stopPropagation();
 ※親要素のイベントが発生しない
 
 デフォルトアクションを中止
 e.preventDefault();
}

~.addEventListener("click" , myFunc, ~);

独自イベント

独自イベント用delegate宣言
delegate void MyDelegator();

class MyClass
{
 独自のイベント
 public event MyDelegator SomeEvent;

 public void Fire()
 {
  if (this.SomeEvent == null) return;
  イベント発生
  SomeEvent();
 }
}

static void Main(string[] args)
{

 MyClass m = new MyClass();
 m.SomeEvent += delegate { Console.WriteLine(~); };
 m.Fire();
}

eventキーワード無しの場合

独自イベント用delegate宣言
delegate void MyDelegator();

class MyClass
{
 独自のイベント(eventキーワード無し)
 public MyDelegator SomeEvent;

 public void Fire()
 {
  if (this.SomeEvent == null) return;
  イベント発生
  SomeEvent();
 }
}

static void Main(string[] args)
{

 MyClass m = new MyClass();
 
 +=、-= によるイベント登録(リスト化)が強制される
 m.SomeEvent = MyFunc :×;
 m.SomeEvent = delegate { return; } :×
;
 
 m.SomeEvent += MyFunc; :○;
 m.SomeEvent += delegate { return; }; :○;
 
 m.Fire();
}

イベントリスナー

addEventListenerの第三引数(true/false)
<div id="myDiv">
 <button id="myButton"></button>
</div>

<script>
 var myDiv = document.getElementById('myDiv');
 var myButton = document.getElementById('myButton');
 
 【falseの場合】
 myDiv.addEventListener("click" , 関数Div , false);
 myButton.addEventListener("click" , 関数Button , false);
 ※子(関数Button)→親(関数Div)の順で実行される
 
 【trueの場合】
 myDiv.addEventListener("click" , 関数Div , true);
 myButton.addEventListener("click" , 関数Button , false);
 ※親(関数Div)→子(関数Button)の順で実行される
 ※trueが優先して実行される

</script>

イベントオブジェクト
イベントリスナーの発生時に引数を自動で取得できる。
イベントオブジェクト:eにはイベント情報が格納されている
var myFunc = function(e){
 
 イベントリスナーが発生したタグ名を取得
 var myTag = e.target.tagName;
 
 イベントフローを中断(デフォルトアクションは実行される)
 e.stopPropagation();
 ※親要素のイベントが発生しない
 
 デフォルトアクションを中止
 e.preventDefault();
}

~.addEventListener(イベント名, myFunc, ~);
※呼ぶ側では、~.addEventListener(イベント名, myFunc(e), ~); としない

WithEvents

Public Class myForm

 Private Class clsEvent
  Public Event TestEvent()
  Public Sub CauseEvent()
   RaiseEvent TestEvent()
  End Sub
 End Class

 ’イベントをインスタンス化
 Private WithEvents myEvent As New clsEvent

 Private Sub myForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  Call myEvent.CauseEvent()
 End Sub

 Private Sub CatchEvent() Handles myEvent.TestEvent
  ’このサブルーチンが呼ばれる
 End Sub

End Class

Delegateとeventの違い

//publicなdelegateを作成
public delegate int LonlyDelegator(string str);

//event型delegateを作成
private class clsDelegate{
  public delegate int Delegator(string str);

  //event付きでインスタンスを作成
  public event Delegator delegator;
 
  //委譲処理実行用メソッド
  public void CauseEvent(string str)
  {
    if (delegator == null) { return; }
    //委譲した処理を実行
    delegator(str);
  }
}
//EventHandlerをevent宣言する意味は全く不明…。
public event EventHandler Eventer;

private void myForm_Load(object sender, EventArgs e)
{
  //Publicなdelegateのインスタンスを作成
  LonlyDelegator myLonlyDelegate = new LonlyDelegator(this.myFunction1);
  //↓処理はevent型ではエラー
  myLonlyDelegate = this.myFunction2;
  //↓処理はevent型ではエラー
  myLonlyDelegate(“Hello”);
 
  //delegate用クラスのインスタンスを作成
  clsDelegate myDelegate = new clsDelegate();
  
  //event制限①:+=でないとイベントを追加できなくなる。
  myDelegate.delegator += this.myFunction1;
  myDelegate.delegator += this.myFunction2;
 
  //event制限②:delegate宣言したクラス内で委譲した処理を実行
  //(myDelegate.delegator(“Hello”); の様に書いて直接委譲した処理を実行できない)
  myDelegate.CauseEvent(“Hello”);
  
  
  Eventer += new EventHandler(this.myFunction3);
  Eventer(this, new EventArgs());
  
  //↓EventHandlerの定義(delegateとして宣言されており、引数も固定されている)
  //public delegate void EventHandler(object sender, EventArgs e);
  EventHandler myEvent = new EventHandler(this.myFunction3);
  myEvent += this.myFunction4;
  myEvent(this, new EventArgs());
  
}

private int myFunction1(string str)
{
  return 1;
}
private int myFunction2(string str)
{
  return 2;
}

//EventHandlerで発生させるイベントは↓の引数を持つ必要がある
private void myFunction3(object sender, EventArgs e)
{
  int i = 5;
}
private void myFunction4(object sender, EventArgs e)
{
  int i = 10;
}

Windowsフォーム

イベント発生順序

【Application起動時】

Control.HandleCreated
コントロールに対してハンドルが作成されると発生

Control.BindingContextChanged
BindingContext プロパティの値が変化したときに発生
フォームのイニシャライズ時にコンテキストにそのTextBoxがバインドしたときに発生 (TextBoxコントロールのBindingContextChangedイベントについて)

Form.Load
フォームが初めて表示される直前に発生

Control.VisibleChanged
Visible プロパティの値が変更された場合に発生

Form.Activated
フォームがコード内またはユーザーの操作によってアクティブになると発生

Form.Shown
フォームが初めて表示されるたびに発生

【Application終了時】

Form.Closing
フォームが閉じている間に発生

Form.FormClosing
フォームが閉じる前に発生

Form.Closed
フォームが閉じたときに発生

Form.FormClosed
フォームが閉じた後に発生

Form.Deactivate
フォームがフォーカスを失い、アクティブでなくなると発生

【プログラム起動時:フォームイベント】
Initialize()
Load()
Resize()
Activate()
GotFocus()
Paint()

【マウスイベント】
MouseDown()
MouseUp()
Click()
MouseMove()
DblClick()
MouseUp()

【キーイベント】
※Form.KeyPreview = False 時

(TextBox上で)キー入力

TextBox:KeyDown

TextBox:KeyPress

TextBox:KeyUp

※Form.KeyPreview = True 時

(TextBox上で)キー入力

Form:KeyDown

TextBox:KeyDown

Form:KeyPress

TextBox:KeyPress

Form_KeyUp

TextBox:KeyUp

※Form.KeyPreview = True 時

(TextBox上で)キー入力

Form:KeyDown

TextBox:KeyDown

Form:KeyPress
KeyAscii = 0(キー操作の取り消し)

(TextBox:KeyPress) ※発生しない

Form_KeyUp

TextBox:KeyUp

【フォーカス遷移時】
MouseDown()
Activate()
GotFocus()
MouseUp()
Click()
Paint()

【プログラム終了時:フォームイベント】
QueryUnload()
Unload()
Terminate()

※Unload()しないと、フォームのLoad()イベントは発生しない。

ApplicationExitイベント

Form_Load(~)
{

  //ApplicationExitイベントハンドラを追加
  Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
  
  // Application終了
  Application.ApplicationExit;
}

//ApplicationExitイベントハンドラ
private void Application_ApplicationExit(object sender, EventArgs e)
{
  ここに処理が移る。
  
  //ApplicationExitイベントハンドラを削除
  Application.ApplicationExit -= new EventHandler(Application_ApplicationExit);
}

DoEvents

WindowsOSにおいて、OSに制御を戻す(待機中の処理を実行させる)命令
ForLoop文やタイマー処理等、その処理が終わるまではCPUを専有する処理において使用する。
なおプログラムを中断し、OSが溜まっているタスクを実行した結果、プログラムに影響を与えてしまう危険がある為推奨されない。
マルチスレッド処理が可能な言語では別スレッドで処理する。

マウスカーソル

時間の掛かる処理中に画面表示を変更するテクニック

カーソルを待機カーソルに変更
Cursor.Current = Cursors.WaitCursor;

カーソルを元に戻す
Cursor.Current = Cursors.AppStarting;

DataBindings

private class MyClass : INotifyPropertyChanged
{
 string _myData;
 
 myDataの変更を通知する定義済みdelegate
 public event PropertyChangedEventHandler PropertyChanged;
 protected void notifyPropertyChanged(string info)
 {
  if (this.PropertyChanged != null)
   PropertyChanged(sender: this, e: new PropertyChangedEventArgs(info));
 }
 
 public string myData
 {
  get { return this._myData; }
  set
  {
   if (value != this._myData)
   {
    this._myData = value;
    this.notifyPropertyChanged(info: @"myData");
   }
  }
 }
}

private MyClass _person;

private void Form1_Load(object sender, EventArgs e)
{
 _person = new MyClass();
 
 this.textMyData.DataBindings.Add(
  propertyName: "text",
  dataSource: _person,
  dataMember: "myData");
}

private void button1_Click(object sender, EventArgs e)
{
 string myData = _person.myData;
 ※textMyData.textの変更内容が_person.myDataに格納されている
 
 _person.myData = "変更後";
 ※_person.myDataの変更内容がtextMyData.textに格納される
}