全体もくじ
⇒https://qiita.com/tera1707/items/4fda73d86eded283ec4f
やりたいこと
WPFの画面には、バブリングイベント
とかトンネリングイベント
などがあって、上から下に、下から上に、イベントが伝達している、という浅い知識はあったが、その知識だけではよくわからない現象があった。
具体的には、ボタンのコントロール<Button>
にMouseLeftButtonDown="Button_MouseLeftButtonDown"
てな感じでマウスの左ボタンを押したときの処理を書いたが、どうもそのButton_MouseLeftButtonDown
メソッドは通ってくれないっぽい。
なぜなのか調べたい。
やったこと
各イベントのハンドラを追加して、そこを通るかどうかを確かめていった。
見たイベント
ハンドラは、下記のものをセットした。
実験プログラム
コード
セットしたハンドラを通ったら、Debug.WriteLineでどのメソッドを通ったか出力するようにしている。
※今回の実験には関係ないが、IsManipulationEnabled
をONOFFするためのボタンも付けている。
<Window x:Class="WpfApp64.MainWindow"
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:WpfApp64"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid x:Name="MainGrid"
Grid.Row="0"
Background="Red"
Margin="50"
IsManipulationEnabled="True"
ManipulationStarting="MainGrid_ManipulationStarting"
ManipulationDelta="MainGrid_ManipulationDelta"
ManipulationCompleted="MainGrid_ManipulationCompleted"
PreviewMouseDown= "MainGrid_PreviewMouseDown" MouseDown="MainGrid_MouseDown"
PreviewMouseUp= "MainGrid_PreviewMouseUp" MouseUp="MainGrid_MouseUp"
PreviewTouchDown= "MainGrid_PreviewTouchDown" TouchDown="MainGrid_TouchDown"
PreviewTouchUp= "MainGrid_PreviewTouchUp" TouchUp="MainGrid_TouchUp"
PreviewStylusDown= "MainGrid_PreviewStylusDown" StylusDown="MainGrid_StylusDown"
PreviewStylusUp= "MainGrid_PreviewStylusUp" StylusUp="MainGrid_StylusUp"
PreviewMouseLeftButtonDown= "MainGrid_PreviewMouseLeftButtonDown" MouseLeftButtonDown="MainGrid_MouseLeftButtonDown"
PreviewMouseLeftButtonUp= "MainGrid_PreviewMouseLeftButtonUp" MouseLeftButtonUp="MainGrid_MouseLeftButtonUp"
PreviewMouseRightButtonDown= "MainGrid_PreviewMouseRightButtonDown" MouseRightButtonDown="MainGrid_MouseRightButtonDown"
PreviewMouseRightButtonUp= "MainGrid_PreviewMouseRightButtonUp" MouseRightButtonUp="MainGrid_MouseRightButtonUp">
</Grid>
<Button Margin="60" Content="ボタン"
Grid.Row="1"
Click="Button_Click"
PreviewMouseDown="Button_PreviewMouseDown" MouseDown="Button_MouseDown"
PreviewMouseUp="Button_PreviewMouseUp" MouseUp="Button_MouseUp"
PreviewTouchDown="Button_PreviewTouchDown" TouchDown="Button_TouchDown"
PreviewTouchUp="Button_PreviewTouchUp" TouchUp="Button_TouchUp"
PreviewStylusDown="Button_PreviewStylusDown" StylusDown="Button_StylusDown"
PreviewStylusUp="Button_PreviewStylusUp" StylusUp="Button_StylusUp"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown" MouseLeftButtonDown="Button_MouseLeftButtonDown"
PreviewMouseLeftButtonUp="Button_PreviewMouseLeftButtonUp" MouseLeftButtonUp="Button_MouseLeftButtonUp"
PreviewMouseRightButtonDown="Button_PreviewMouseRightButtonDown" MouseRightButtonDown="Button_MouseRightButtonDown"
PreviewMouseRightButtonUp="Button_PreviewMouseRightButtonUp" MouseRightButtonUp="Button_MouseRightButtonUp"
/>
<Button Name="KirikaeBtn"
Width="150" Height="40"
HorizontalAlignment="Left" VerticalAlignment="Top"
Content="IsManipulationEnabled = true"
Click="IsManipulationEnabledChange"/>
</Grid>
</Window>
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
namespace WpfApp64
{
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
private void Button_Click(object sender, RoutedEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_MouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewTouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_TouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewStylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); /* e.Handled = true; */ }
private void Button_StylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_ManipulationStarting(object sender, ManipulationStartingEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_MouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewTouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_TouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewStylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_StylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewMouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_MouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewTouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_TouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_PreviewStylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void MainGrid_StylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_MouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewTouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_TouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_PreviewStylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void Button_StylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
private void IsManipulationEnabledChange(object sender, RoutedEventArgs e)
{
if (MainGrid.IsManipulationEnabled)
{
MainGrid.IsManipulationEnabled = false;
KirikaeBtn.Content = "false";
}
else
{
MainGrid.IsManipulationEnabled = true;
KirikaeBtn.Content = "true";
}
}
}
}
画面
実験プログラムは下記のような画面。
- 左上のボタンは、赤い領域のGridの
IsManipulationEnabled
をONOFFするためのボタン(今回は関係ない) - 赤い部分は、Gridに対して上に挙げたイベントのハンドラをセットした部分。
- 下の細長いボタンは、Buttonに対して上に挙げたイベントのハンドラをセットした部分。
結果
下のButton部分を指でタッチしたとき
出力
下記のような結果になった。
Button_PreviewStylusDown
Button_StylusDown
Button_PreviewTouchDown
Button_TouchDown
Button_PreviewMouseLeftButtonDown
Button_PreviewMouseDown
Button_PreviewStylusUp
Button_StylusUp
Button_PreviewTouchUp
Button_TouchUp
Button_PreviewMouseLeftButtonUp
Button_PreviewMouseUp
Button_Click
結果、Preview〇〇〇
イベントがきたら、対応する〇〇〇
イベントが来ているイベントもあるが、そうでないものもある。下記のようなクセがあるなと感じた。
クセがあるなと思う点その①
イメージとして、Preview〇〇〇
イベントがきたら、対応する〇〇〇
イベントが来るイメージだが、PreviewMouseLeftButtonDown
とPreviewMouseDown
については、Previewが2つ連続して来ている。
PreviewMouseDownとPreviewMouseLeftButtonDownまたはPreviewMouseRightButtonDownは一緒に来る
とのこと。ゆえに、
PreviewのあとにPreviewなしが来るのではなく、連続してその2つのPreviewがくる
っぽい。
クセがあるなと思う点その②
下記の4つのトンネリングイベント(Preview_〇〇〇)に対するバブリングイベント(Previewない奴)を通っていない
- PreviewMouseLeftButtonDown
- PreviewMouseDown
- PreviewMouseLeftButtonUp
- PreviewMouseUp
公式のClickイベントの解説によると、
ButtonBase
を継承しているもの(Buttonがそれ)では、MouseLeftButtonDown
がhandledとして扱われるので、MouseLeftButtonDown
イベントは発生しない。
代わりにPreviewMouseLeftButtonDown
を使えますよ。
とのこと。
その説明だけでは上の結果すべての謎が解けるわけではなかったが、動きからすると、
<Button>
では、PreviewMouseLeftButtonDown
に対応するMouseLeftButtonDown
など、マウスを押したとき系イベントのトンネリングイベントは来るが、バブリングイベントは来ない
ということっぽい。
上のやりたいことに書いた、Button_MouseLeftButtonDownメソッドは通ってくれない
というのは、これが原因っぽい。
その他、気づいたこと
- 指でタッチすると、指のタッチやスタイラスのタッチのイベントが来た後に、マウスのクリックのイベントが来る。
⇒タッチとマウスで同じことをさせたければ、マウスのイベントやClickイベントに処理を書けばよくて、タッチ時にタッチ特有の処理をさせたければ、タッチのイベントに処理を書けばよいっぽい。 -
Click
イベントは、マウスがDown⇒Upしたあとにくる。
⇒マウスのイベントが一通り片付いた後に、Clickとしての処理をするということか。
⇒基本、ボタンを押した時
の処理はClickに書いて、ボタンではない、画面上のどこかの領域を押した、というときは、MouseDownなどのイベントに処理を書けばよいっぽい。
下のボタン部分をマウスでクリックしたとき
上では指でタッチをした時の動作を見たが、マウスでクリックしたときの動作も見た。
出力
Button_PreviewMouseLeftButtonDown
Button_PreviewMouseDown
Button_PreviewMouseLeftButtonUp
Button_PreviewMouseUp
Button_Click
指でタッチしたときの結果から、Stylusなんとか
やTouchなんとか
を抜いた形。
マウスでボタンをクリックしたときも、やはりバブリングイベントは来ない。
これはイメージ通り。