このMSDNの記事(とりわけ)によると、
クラスハンドラーは、ルーティングされたイベントがそのルート内の要素インスタンスに到達するたびに、そのクラスのインスタンスにアタッチされているインスタンスリスナーハンドラーの前に呼び出されます。
私はRoutedEvent
sにまったく慣れていないので、コードに間違いがある可能性がありますが、RoutedEvent
として宣言されたにアタッチされたクラスハンドラーは、同じイベントにアタッチされたインスタンスハンドラーの前に常に起動するとは限らないRoutingStrategy.Tunnel
ようです。
TouchButton
以下の例では、トンネリングRoutedEvent
とバブリングを使用してコントロールクラスを作成しましたRoutedEvent
。それぞれにクラスハンドラーを登録しました。次に、ウィンドウにクラスのインスタンスを作成し、コードビハインドで各イベントを処理します。クラス要素とそれを含むの両方で、トンネリングイベントに同じハンドラーをアタッチしましたGrid
。4つのハンドラーすべてに名前が表示さMessageBox
れるため、実行の順序を明確に確認できます。
- グリッドインスタンスPreviewTouch
- クラスTouchButton_PreviewTouch
- TouchButtonインスタンスPreviewTouch
- クラスTouchButton_Touch
- タッチボタンインスタンスタッチ
これはe.Handled = true;
、クラスPreviewTouch
イベントハンドラーを呼び出すと、要素にアタッチされているものを除いて、実行が他のすべてのイベントハンドラーに到達するのを停止できることを意味しGrid
ます。これはこんな感じなのか、どこかで間違えたのか?それ以外の場合、実行がすべてのインスタンスイベントハンドラーに到達するのを停止するにはどうすればよいですか?
クラスは次のとおりです。
public class TouchButton : Button
{
static TouchButton()
{
EventManager.RegisterClassHandler(typeof(TouchButton), PreviewTouchEvent,
new RoutedEventHandler(TouchButton_PreviewTouch), true);
EventManager.RegisterClassHandler(typeof(TouchButton), TouchEvent,
new RoutedEventHandler(TouchButton_Touch), true);
}
private static void TouchButton_PreviewTouch(object sender, RoutedEventArgs e)
{
MessageBox.Show("Class TouchButton_PreviewTouch");
}
private static void TouchButton_Touch(object sender, RoutedEventArgs e)
{
MessageBox.Show("Class TouchButton_Touch");
}
public static RoutedEvent TouchEvent = EventManager.RegisterRoutedEvent("Touch",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TouchButton));
public event RoutedEventHandler Touch
{
add { AddHandler(TouchEvent, value); }
remove { RemoveHandler(TouchEvent, value); }
}
public static RoutedEvent PreviewTouchEvent = EventManager.RegisterRoutedEvent(
"PreviewTouch", RoutingStrategy.Tunnel, typeof(RoutedEventHandler),
typeof(TouchButton));
public event RoutedEventHandler PreviewTouch
{
add { AddHandler(PreviewTouchEvent, value); }
remove { RemoveHandler(PreviewTouchEvent, value); }
}
protected override void OnClick()
{
RaiseTouchEvent();
}
private void RaiseTouchEvent()
{
RoutedEventArgs touchEventArgs = new RoutedEventArgs(PreviewTouchEvent);
RaiseEvent(touchEventArgs);
if (!touchEventArgs.Handled) RaiseEvent(new RoutedEventArgs(TouchEvent));
}
}
背後にあるウィンドウコードのインスタンスハンドラは次のとおりです。
private void TouchButton_PreviewTouch(object sender, RoutedEventArgs e)
{
MessageBox.Show(string.Format("{0} Instance PreviewTouch",
((FrameworkElement)sender).Name));
}
private void TouchButton_Touch(object sender, RoutedEventArgs e)
{
MessageBox.Show(string.Format("{0} Instance Touch",
((FrameworkElement)sender).Name));
}
コントロールXAMLは次のとおりです。
<Grid Name="Grid" Controls:TouchButton.PreviewTouch="TouchButton_PreviewTouch">
<Controls:TouchButton x:Name="TouchButton" Width="200" Height="45" FontSize="24"
Content="Touch me" Touch="TouchButton_Touch" PreviewTouch="TouchButton_PreviewTouch" />
</Grid>
トンネリングイベントは、Grid
要素に「トンネリング」する前に要素によって処理されることを理解してTouchButton
いますが、クラスハンドラーは常にインスタンスハンドラーの前に起動することになっていると思いました。そうでない場合、どうすればこれを達成できますか?
更新>>>
@sanguineの回答のおかげで、すべてのインスタンスハンドラーがイベントを処理しないようにする方法を見つけることができました。宣言されたクラス処理タイプをTouchButton
sanguineGrid
が提案したように置き換える代わりに、に置き換えると、すべての派生コントロールFrameworkElement
をキャッチします。FrameworkElement
EventManager.RegisterClassHandler(typeof(FrameworkElement), PreviewTouchEvent,
new RoutedEventHandler(TouchButton_PreviewTouch), true);