その1:http://blogs.wankuma.com/kazuki/archive/2008/02/21/124157.aspx
その2と名乗りつつ前の続きではないという罠。
WPFのイベントはルーティングイベントとかいってWindowsFormsのイベントとはちょっと違う動きをする。
WindowsFormsのイベントと同じだとちょっと困る例を示してみようと思う。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace WpfEventIntro
{
public class MyApplication : Application
{
public MyApplication()
{
Startup += (sender, e) =>
{
HelloEventWindow window = new HelloEventWindow();
window.Show();
};
}
[STAThread]
static void Main(string[] args)
{
new MyApplication().Run();
}
}
public class HelloEventWindow : Window
{
public HelloEventWindow()
{
Init();
}
private void Init()
{
// 画面組み立て
var stackPanel = new StackPanel();
Content = stackPanel;
var btn1 = new Button();
var current = btn1;
for (int i = 0; i < 10; i++)
{
var btn2 = new Button();
current.Content = btn2;
current = btn2;
}
stackPanel.Children.Add(btn1);
}
}
}
とりあえずこんなコード。HelloEventWindowのInitメソッドで画面の組み立てをやってる。forループを回してボタンの中にボタンを10個入れ子にしてみた。
実行すると、下のような普通はお目にかかれないような雰囲気のものが出来上がる。
これの一番下のボタンにクリックイベントをこさえる。
private void Init()
{
// 画面組み立て
var stackPanel = new StackPanel();
Content = stackPanel;
var btn1 = new Button();
// イベントですよ
btn1.Click += (sender, e) => MessageBox.Show("押されたんじゃ~");
var current = btn1;
for (int i = 0; i < 10; i++)
{
var btn2 = new Button();
current.Content = btn2;
current = btn2;
}
stackPanel.Children.Add(btn1);
}
たったこれだけで、11個あるボタンのどれを押しても下のようにメッセージボックスが出る。
WPFのイベントは、イベントが起きると親へ親へとイベントを伝播していく。
因みに、イベントが親へ親へ伝播されるまえに、Preview~という名前のイベントも起きたりする。こっちは親から子へ伝播する。
というわけで、ボタンの中にあるボタンを押下しても、クリックイベントは親へ親へ渡され、いずれボタンクリックイベントが登録されてるbtn1へたどり着くって寸法です。
こういう動きをすることで、いろいろな要素が合成されて作られたものでも、今までと同じ感覚でイベントを記述することができるようになってるという寸法。
こんな動きをするイベントだから、WindowsFormsと同じようにコントロール作ってイベント作ってとかやっても同じように動かない。
WPFのイベントの仕組みのお作法に従う必要がある。
お作法ってのは、DependencyPropertyを定義するときとほとんど同じでいけます。
ためしにHogeというイベントを上記のウィンドウに追加する場合は下のようになります。
public static readonly RoutedEvent HogeEvent = EventManager.RegisterRoutedEvent(
"Hoge", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HelloEventWindow));
EventManagerに登録ってやればOK。
んで、これだけじゃあんまりなので既存のC#で作ったクラスのイベントみたいに扱えるようなコードを書き足します。
public static readonly RoutedEvent HogeEvent = EventManager.RegisterRoutedEvent(
"Hoge", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HelloEventWindow));
public event RoutedEventHandler Hoge
{
add { AddHandler(HogeEvent, value); }
remove { RemoveHandler(HogeEvent, value); }
}
これでイベントHoge完成!通常のC#のようなイベントの登録方法でもいけるし、AddHandlerメソッドを使ってWindowとか、まったく関係ない人に処理をお願いしてもOKだろう。