プログラム言語 イベント、委譲
目次
Delegate
基本
delegate = 処理を格納する変数
≒関数ポインタ(C++)
{
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 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
マルチキャスト
{
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 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 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 query = ary.Where(delegate(int prm) {
return prm > 5;
});
foreach (var elm in query) Console.WriteLine(elm);
→6 7 8 9 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 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 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();
}
イベントリスナー
<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
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 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フォーム
イベント発生順序
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イベント
{
//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
{
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に格納される
}