CLRイベントとWPF Routedイベントの違い

いきなりですが問題です。CLRイベントとWPFのRoutedイベント、Routedイベントにはトンネル/バブルモードはありますが、それ以外に違いはあるでしょうか?

はい、なにRoutedイベントはXAMLで指定できるがCLRイベントはXAMLで指定できない? えー残念ながら間違いです。例を見ながら確認しておきましょう。

ButtonWithClrEvent.cs

namespace RoutedEventAndClrEvent
{
    using System;
    using System.Windows.Controls;
    public class ButtonWithClrEvent : Button
    {
        // CLR Event
        public event EventHandler SampleClrEvent = (_, __) => { };

        // click -> Some clr event
        protected override void OnClick()
        {
            SampleClrEvent(this, EventArgs.Empty);
            base.OnClick();
        }
    }
}

これは普通のイベントですね。後の確認用に、clickイベントで該イベントを発砲しておきます。

ButtonWithRoutedEvent.cs

namespace RoutedEventAndClrEvent
{
    using System.Windows;
    using System.Windows.Controls;
    public class ButtonWithRoutedEvent : Button
    {
        // Routed Event
        public static readonly RoutedEvent SampleRoutedEvent =
            EventManager.RegisterRoutedEvent("SampleRouted",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(ButtonWithRoutedEvent));

        // CLR accessors
        public event RoutedEventHandler SampleRouted
        {
            add { AddHandler(SampleRoutedEvent, value); }
            remove { RemoveHandler(SampleRoutedEvent, value); }
        }

        // click -> RoutedEvent
        protected override void OnClick()
        {
            RaiseEvent(new RoutedEventArgs(SampleRoutedEvent));
            base.OnClick();
        }
    }
}

次いでRoutedイベント(日本語で読むときはルーティングイベント)、CLRアクセサでAddHandler, RemoveHandlerをラップするのは定石です。まずこれらがXAMLで指定できるか?というところを確認しておきます。

 MainWindow.xaml

<Window x:Class="RoutedEventAndClrEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:me="clr-namespace:RoutedEventAndClrEvent"
        Title="MainWindow" Height="138" Width="235">
    <StackPanel>
        <me:ButtonWithRoutedEvent x:Name="RoutedEventButton" SampleRouted="SampleRoutedEventHandler">Routed Event</me:ButtonWithRoutedEvent>
        <me:ButtonWithClrEvent x:Name="ClrEventButton" SampleClrEvent="SampleClrEventHanlder">CLR Event</me:ButtonWithClrEvent>
    </StackPanel>
</Window>

 MainWindow.xaml.cs

namespace RoutedEventAndClrEvent
{
    using System;
    using System.Windows;
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        // Routedイベントハンドラ
        private void SampleRoutedEventHandler(object sender, RoutedEventArgs e){
            MessageBox.Show("Routed Event Occured int the instance context.");
        }

        // CLRイベントハンドラ
        private void SampleClrEventHanlder(object sender, EventArgs e){
            MessageBox.Show("CLR Event Occured int the instance context.");
        }
    }
}

mainwindow

問題なく動作します。

ではCLRイベントとRoutedイベントは何が違うのでしょうか?上の例ではイベントハンドラにインスタンスメソッドを割り当てていますが、staticメソッドを割り当てた時に違いが見えてきます。大量のコピペになるので省略しますが、両ハンドラを単純にstaticにしてみてください。いずれも実行時にXAMLエラーが発生します。それぞれstaticメソッドをアタッチするときは下のようにします。

 MainWindow.xaml.cs

namespace RoutedEventAndClrEvent
{
    using System;
    using System.Windows;
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            RoutedEventButton.SampleRouted += SampleRoutedEventHandler;

            // Routedイベントハンドラ<クラスレベルハンドラ>
            EventManager.RegisterClassHandler(
                typeof(ButtonWithRoutedEvent),
                ButtonWithRoutedEvent.SampleRoutedEvent,
                new RoutedEventHandler(SampleStaticRoutedEventHandler));
            // CLRイベントハンドラ
            ClrEventButton.SampleClrEvent += SampleStaticClrEventHanlder;
        }

        // Routedイベントハンドラ
        private static void SampleStaticRoutedEventHandler(object sender, RoutedEventArgs e){
            MessageBox.Show("Routed Event Occured in the static context.");
        }
        private void SampleRoutedEventHandler(object sender, RoutedEventArgs e){
            MessageBox.Show("Routed Event Occured int the instance context.");
        }

        // CLRイベントハンドラ
        private static void SampleStaticClrEventHanlder(object sender, EventArgs e){
            MessageBox.Show("CLR Event Occured int the static context.");
        }
    }
}

CLRイベントのほうは何も変わっていないので良いとして、Routedイベントはクラスレベルハンドラというものが登場します。これがCLRイベントとRoutedイベントの違いです!

クラスレベルハンドラとは、他のインスタンスハンドラよりも先だって呼ばれるハンドラです。RoutedEventはRoutedEventArgs内のHandledをtrueにするとイベントルーティングを停止させられますが、当然クラスレベルハンドラにもあります。よって各インスタンスハンドラに渡る前に統一的に処理をする場合に使えるということです。トンネルとバブルも含めてイベントの発生順を図示すると以下のようになります。

event

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中