[WPF] Buttonをマウスクリック/タッチしたときのイベントの流れ(親子関係なし)

**全体もくじ**
⇒https://qiita.com/tera1707/items/4fda73d86eded283ec4f

**イベント関連のもくじ**
⇒https://qiita.com/tera1707/items/4fda73d86eded283ec4f#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E9%96%A2%E9%80%A3wpfxaml

# やりたいこと

WPFの画面には、`バブリングイベント`とか`トンネリングイベント`などがあって、上から下に、下から上に、イベントが伝達している、という浅い知識はあったが、その知識だけではよくわからない現象があった。

具体的には、ボタンのコントロール`<Button>`に`MouseLeftButtonDown="Button_MouseLeftButtonDown"`てな感じでマウスの左ボタンを押したときの処理を書いたが、どうもその`Button_MouseLeftButtonDown`メソッドは通ってくれないっぽい。

なぜなのか調べたい。

# やったこと

各イベントのハンドラを追加して、そこを通るかどうかを確かめていった。

### 見たイベント

ハンドラは、下記のものをセットした。

|トンネリング|バブリング|備考|
|:--|:--|:--|
|[PreviewMouseDown](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewmousedown?view=netcore-3.1)|[MouseDown](https://docs.microsoft.com/en-us/dotnet/api/system.windows.uielement.mousedown?view=netcore-3.1)||
|[PreviewMouseUp](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewmouseup?view=netcore-3.1)|[MouseUp](https://docs.microsoft.com/en-us/dotnet/api/system.windows.uielement.mouseup?view=netcore-3.1)||
|[PreviewTouchDown](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewtouchdown?view=netcore-3.1)|[TouchDown](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.touchdown?view=netcore-3.1)||
|[PreviewTouchUp](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewtouchup?view=netcore-3.1)|[TouchUp](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.touchup?view=netcore-3.1)||
|[PreviewStylusDown](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewstylusdown?view=netcore-3.1)|[StylusDown](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.stylusdown?view=netcore-3.1)||
|[PreviewStylusUp](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewstylusup?view=netcore-3.1)|[StylusUp](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.stylusup?view=netcore-3.1)||
|[PreviewMouseLeftButtonDown](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewmouseleftbuttondown?view=netcore-3.1)|[MouseLeftButtonDown](https://docs.microsoft.com/en-us/dotnet/api/system.windows.uielement.mouseleftbuttondown?view=netcore-3.1)||
|[PreviewMouseLeftButtonUp](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.uielement.previewmouseleftbuttonup?view=netcore-3.1)|[MouseLeftButtonUp](https://docs.microsoft.com/en-us/dotnet/api/system.windows.uielement.mouseleftbuttonup?view=netcore-3.1)||
|[PreviewMouseRightButtonDown]()|[MouseRightButtonDown]()||
|[PreviewMouseRightButtonUp]()|[MouseRightButtonUp]()||
|なし|[Click](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.controls.primitives.buttonbase.click?view=netcore-3.1)|`<Button>に対してのセット`|

## 実験プログラム

### コード

セットしたハンドラを通ったら、Debug.WriteLineでどのメソッドを通ったか出力するようにしている。
※今回の実験には関係ないが、`IsManipulationEnabled`をONOFFするためのボタンも付けている。

``` MainWindow.xaml
<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>

```

```MainWindow.xaml.cs
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に対して上に挙げたイベントのハンドラをセットした部分。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/03440b18-78a1-9640-3130-bc2aa267f877.png)


## 結果 

### 下の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
```


絵で描くと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/04988799-ffe0-60ce-488a-7293f66b9f49.png)


結果、`Preview〇〇〇`イベントがきたら、対応する`〇〇〇`イベントが来ているイベントもあるが、そうでないものもある。下記のようなクセがあるなと感じた。

#### クセがあるなと思う点その①
イメージとして、`Preview〇〇〇`イベントがきたら、対応する`〇〇〇`イベントが来るイメージだが、`PreviewMouseLeftButtonDown`と`PreviewMouseDown`については、**Preview**が2つ連続して来ている。

[公式のPreviewMouseDownの解説](https://docs.microsoft.com/en-us/dotnet/api/system.windows.uielement.previewmousedown?view=netcore-3.1#remarks)によると、

> PreviewMouseDownとPreviewMouseLeftButtonDownまたはPreviewMouseRightButtonDownは一緒に来る

とのこと。ゆえに、
**PreviewのあとにPreviewなしが来るのではなく、連続してその2つのPreviewがくる**
っぽい。

#### クセがあるなと思う点その②

下記の4つのトンネリングイベント(Preview_〇〇〇)に対するバブリングイベント(Previewない奴)を通っていない

- PreviewMouseLeftButtonDown
- PreviewMouseDown
- PreviewMouseLeftButtonUp
- PreviewMouseUp

[公式のClickイベントの解説](https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.buttonbase.click?view=netcore-3.1#remarks)によると、

> `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
```

絵で描くと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/61c5e000-2217-36e7-43f6-629b6ac808a3.png)

指でタッチしたときの結果から、`Stylusなんとか`や`Touchなんとか`を抜いた形。
マウスでボタンをクリックしたときも、やはりバブリングイベントは来ない。
これはイメージ通り。