VB のたまご

作成日: 2017/03/07, 更新日: 2017/03/07



ルーティングイベント1

  •  WPF のイベントはルーティングイベントと呼ばれています。 WinForms のイベントとどう違うのか見てみましょう。

  • スポンサーリンク


イベント処理のおさらい

  •  まずはおさらいです。 WinForms 時代や今までのコントロールの説明では、1コントロールに付き1イベントハンドラを紐づけていました。 たまに、複数コントロールを1つのイベントハンドラに結び付けて共通処理をまとめていましたが、基本的には、1コントロールに付き1イベントハンドラでした。

  • <Window x:Class="RoutedEventWindow1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="RoutedEventWindow1" Height="300" Width="300">
     
        <Grid>
    
            <Button Content="button1" Click="Button_Click" />
    
        </Grid>
        
    </Window>
    

    Public Class RoutedEventWindow1
    
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    
            Console.WriteLine("Button_Click")
    
        End Sub
    
    End Class
    

  •  ボタンイベントが発生しましたね。 次にルーティングイベントを見てみましょう。 私の環境では、Grid で Button.Click を入力する際、候補が表れないのに加えて別の候補を選択させられてしまうため、この時だけは Esc キーを押してインテリセンスを消してから、入力しています。

  • <Window x:Class="RoutedEventWindow2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="RoutedEventWindow2" Height="300" Width="300">
       
        <Grid Name="grid1" Button.Click="Button_Click">
    
            <StackPanel Name="stackpanel1" Margin="10" Button.Click="Button_Click">
    
                <Button Name="button1" Content="button1" Click="Button_Click" />
                <Button Name="button2" Content="button2" Click="Button_Click" />
    
            </StackPanel>
            
        </Grid>
        
    </Window>
    

    Public Class RoutedEventWindow2
    
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    
            Dim calledControl = CType(sender, FrameworkElement)
            Console.WriteLine($"{calledControl.Name}, {calledControl.GetType()}")
    
        End Sub
    
    End Class
    

  •  これを実行して、例えば button1 の方のボタンを押します。以下のように3つ出力されます。

  • 出力結果
    button1, System.Windows.Controls.Button
    stackpanel1, System.Windows.Controls.StackPanel
    grid1, System.Windows.Controls.Grid
    

  •  3つ出力されたということは、3つのコントロールがボタンイベントを処理したことになります。 StackPanel や Grid は Button ではないですが、Button.Click=Button_Click と紐づけただけでボタンイベントを拾っていて、 button1 の Button は出力されていませんね。

  •  ルーティングイベントは、運動会でやる学年別リレーのバトン渡しみたいなものです。 発生元コントロールでイベント発生した際、発生元コントロールでイベントが終わるのではなく、その親コントロール、さらにその親コントロール、、、と、 ルートコントロールまでイベントが昇っていきます。この動き方が、沸騰したお湯の中で泡(バブル)が昇るように見えることから(というのは私の解釈ですが)「バブルイベント」と呼ばれています。

  • <Grid Name="grid1" Button.Click="Button_Click"> ←③イベントキャッチと処理
    
        <StackPanel Name="stackpanel1" Margin="10" Button.Click="Button_Click"> ←②イベントキャッチと処理
    
            <Button Name="button1" Content="button1" Click="Button_Click" /> ←①イベント発生、イベントキャッチと処理
            <Button Name="button2" Content="button2" Click="Button_Click" />
    
        </StackPanel>
        
    </Grid>
    

    スポンサーリンク


バブルイベント

  •  それではバブルイベントを使ったサンプルです。 人それぞれかもしれませんが、1つ1つにイベント処理を書いていくよりも、親コントロールで集中管理するように、 1つだけチェックイベントを書いた方がシンプル(な場合もあると思う)です。

  • <Window x:Class="RoutedEventWindow3"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="RoutedEventWindow3" Height="300" Width="300">
        
        <Grid>
    
            <StackPanel Margin="10" Background="AliceBlue"
                        CheckBox.Checked="CheckBox_Checked">
                
                <CheckBox Name="checkbox1" Content="バナナ" />
                <CheckBox Name="checkbox2" Content="リンゴ" />
                <CheckBox Name="checkbox3" Content="ヨーグルト" />
           
            </StackPanel>
            
        </Grid>
        
    </Window>
    

    Public Class RoutedEventWindow3
    
        Private Sub CheckBox_Checked(sender As Object, e As RoutedEventArgs)
    
            ' sender だとバブルイベント毎に呼び出し元が変わってくる(sender は、キャッチ先コントロールみたいな感じ)
            Dim calledControl = TryCast(e.Source, CheckBox)
            If calledControl Is Nothing Then
                Exit Sub
            End If
    
            Console.WriteLine($"{calledControl.Name} ({calledControl.Content}) がチェックされた")
    
        End Sub
    
    End Class
    

  •  WinForms の時に利用していた sender ですが、WPF ではイベント発生元コントロールではなくキャッチ先コントロールとして入ってきます。 つまり、上記のサンプルの通り、3回ボタンイベントをキャッチした場合、3回ともそれぞれ別のコントロールが入ってきます。 チェックを付けたコントロールを取得したい場合は、RoutedEventArgs.Source プロパティの方を使います。

  •  実行後、上から順にチェックを付けた際の結果です。

  • 出力結果
    checkbox1 (バナナ) がチェックされた
    checkbox2 (リンゴ) がチェックされた
    checkbox3 (ヨーグルト) がチェックされた
    

  •  ところでバブルイベントを利用しなくても、同じイベントハンドラを3つのチェックボックスでハンドルすることもできます。 1つ1つに記述する手間がありますが。 で、共通処理にすると言うことは、コントロール名など利用して条件別処理を記述することになるということです。 つまり、処理の肥大化になるんじゃない(なりやすいんじゃない)って思ってしまいますが、使い方次第なんでしょうね。