第3回 XAMLコードから生成されるプログラム・コードを理解する ―― XAML(2): WPF固有機能の基礎 ――:連載:WPF入門(4/4 ページ)
XAMLコードから自動生成される中間生成物のプログラム・コード、依存関係プロパティとルーティング・イベントを解説。WPFの内部的な挙動を理解しよう。
■ルーティング・イベント
依存関係プロパティと同様に、WPFでは「ルーティング・イベント(routed event)」と呼ばれる特殊なイベント通知機構を持っている(プロパティの場合と同様に、通常のイベントを「CLRイベント」と呼んで区別する)。ルーティング・イベントは依存関係プロパティのイベント版といえる。
CLRイベントとの差は、発生したイベントが要素ツリーをたどって“ルーティング”されるところにある。ルーティングの方針には以下の3種類がある。
- 直接(Direct): イベント発生源となる要素自身のイベント・ハンドラのみが呼び出される。
- トンネル(Tunnel): 要素ツリーのルートからイベント発生源となる要素に向かって、要素ツリーを掘り進むようにイベント・ハンドラが呼び出される。
- バブル(Bubble): イベント発生源となる要素からルートに向かって、要素ツリーをたどりながら、浮かび上がるようにイベント・ハンドラが呼び出される。
●ルーティング・イベントの定義
List 8にルーティング・イベントの定義例を示す。
public partial class MyControl : UserControl
{
public static readonly RoutedEvent ClickEvent =
EventManager.RegisterRoutedEvent(
"Click", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(MyControl));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
}
Public Class MyControl
Public Shared ReadOnly ClickEvent As RoutedEvent = _
EventManager.RegisterRoutedEvent( _
"Click", RoutingStrategy.Bubble, _
GetType(RoutedEventHandler), GetType(MyControl))
Public Custom Event Click As RoutedEventHandler
AddHandler(ByVal value As RoutedEventHandler)
MyBase.AddHandler(ClickEvent, value)
End AddHandler
RemoveHandler(ByVal value As RoutedEventHandler)
MyBase.RemoveHandler(ClickEvent, value)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
MyBase.RaiseEvent(e)
End RaiseEvent
End Event
End Class
依存関係プロパティと同様に、RoutedEventクラス(System.Windows名前空間)のインスタンス自体は辞書のキー(+メタデータ)のようなものであり、実際にイベント・ハンドラを保持するのはUIElementクラス(System.Windows名前空間)(の子クラス)のインスタンスである。ルーティング・イベントを格納する静的フィールドの名前は「イベント名+Event」とする。
ルーティング・イベントもWPFへの登録が必要で、こちらはEventManagerクラス(System.Windows名前空間)のRegisterRoutedEventメソッドを用いる。引数は、イベント名、ルーティング方針、イベント・ハンドラの型、および、イベント発生源となる型である。
UIElementクラスのインスタンスへのイベント・ハンドラの追加/削除は、UIElement.AddHandler/RemoveHandlerメソッドを用いて行う。依存関係プロパティの定義の際に対応するCLRプロパティを定義したのと同様に、ルーティング・イベントの場合にも対応するCLRイベントを定義しておく(上記の例ではClickイベントが該当)。
●ルーティング・イベントの利用例
ルーティング・イベントを利用することで、UI要素で発生したイベントを、その親要素で一括して処理できる。例えば、<ListBox>要素のような、コレクションとして子要素を持つようなもので、子要素で発生したイベントを<ListBox>要素自身で一括して処理したい場合などに便利である。
List 9にその一例を挙げる。本連載でまだ説明していないデータ・テンプレートなどの機能を使っているが、詳細は次回以降で説明していく。
ここでのポイントは<ListBox>要素でButton.Clickイベントを拾い、子要素として追加した<Button>要素のClickイベントを一括処理している部分である。<Button>要素1つ1つにイベント・ハンドラを追加しなくても、<ListBox>要素側の1度きりの追加でイベントの処理が可能になっている。
<UserControl x:Class="MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ListBox Name="list" Button.Click="Button_Click">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
using System.Windows;
using System.Windows.Controls;
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
this.list.ItemsSource = new[]
{
"A", "B", "C",
};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = e.OriginalSource as Button;
MessageBox.Show(button.DataContext.ToString());
}
}
Public Class MyControl
Public Sub New()
InitializeComponent()
Me.list.ItemsSource = {"A", "B", "C"}
End Sub
Private Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim button As Button = e.OriginalSource
MessageBox.Show(button.DataContext.ToString())
End Sub
End Class
次回はスタイルやテンプレートなどの仕組みについて説明を行っていく。
「連載:WPF入門」
Copyright© Digital Advantage Corp. All Rights Reserved.