こんにちは、働くC#プログラマーのさんさめです。
WPFアプリを作っていると、
思ったようなレイアウトに
なってくれないことがよくあります。
2017以降のVisualStudioであれば、
xamlのエディットコンティニューができるため、
Marginを調整したり、
VerticalAlignmentを変えてみたりなど、
色々試すことで改善できることもあります。
しかし、
xamlでは直接記述していない要素、
すなわちビジュアルツリーのみに存在
している要素の場合は、
エディットコンティニューでは解決が難しいです。
そもそもどうして
そんな見た目になっているのか
見当もつかないこともあります。
そんな時に役に立つのが、
「Snoop(スヌープ)」
というフリーの開発ツールです。
Snoopは、WPFの開発をする上で、
非常に有用です。
本記事では、
Snoopの基本的な使い方や、
活用方法などを具体的に解説していきます。

めちゃくちゃ便利なので、
WPF開発者は全員入れるべきです
Snoopの入手方法
こちらのページからダウンロードできます。
基本的には、安定版であるLatest Release
を使うことを推奨します。
(本記事ではv3.0.1で進めます。
v3以降じゃないと.Net Core対応していないので注意)
パッケージをダウンロードしましょう。

ダウンロードしたmsiを実行すると、
Windowsさんに不穏な警告を出されますが、
気にせず進めます。


インストーラが出るので、
全て既定のままインストールします。

インストール先は変えても構いません。

インストールが終わると、
普通にスタートメニューから実行できるようになります。

基本的な使い方
ダウンロードが終わったら、
さっそくexeを実行してみましょう。
次のようなバーがディスプレイの
どこかに出現しているはずです。

これは、いわばSnoopの待機状態です。
この状態で、
WPFアプリケーションをデバッグ実行してみましょう。
…まだ、何も起きませんね。
では次に、
以下の赤丸で囲った部分をドラッグし、
そのまま先ほどデバッグ実行した、
WPFアプリケーションにドロップします。

すると、
snoopがアプリケーションにアタッチされて、
以下のように新規ウィンドウが表示されます。
(gifアニメです)

このウィンドウは、
アプリケーションのビジュアルツリーを
可視化したものです。
…と言われても、
どういうことかまだ分かりませんよね。
さて、次に、
WPFアプリの
見た目が思うようになっていない要素に、
マウスカーソルを合わせてください。
そして、その状態で、
「Ctrl」と「Shift」を同時押しします(!?)
「Ctrl」と「Shift」のみですよ。

すると、どうでしょう。
次のように先ほど開いたウィンドウの
ツリーが展開されて、
マウスを合わせていたコントロールが
選択された状態になります。

…え?xamlに記述した要素と違いますか?
おそらく、大抵の場合はそうなります。
これは、xamlに記述した要素よりも、
ビジュアルツリーというものが
はるかに色々な要素を含んでいるものだからです。
ちょっと意味が良く分からないかもしれませんが、
xamlに記述した要素をもとに、
WPFというフレームワークが
必要なUI要素を展開したものが、
「ビジュアルツリー」だと思ってください。
(※ちなみに、
xamlに記述した要素群は論理ツリーと呼ばれます)
話を戻しますと、
ビジュアルツリーの方が要素が多いため、
目的のものと微妙に合致しないものに
フォーカスしてしまうことが多いのですね。
少しだけツリーの祖先を辿っていくと、
xamlに書いた要素が見つかるはずです。

…さて、
問題の要素が見つかったら、
次はウィンドウの右側に注目します。
ウィンドウの右側は、
いくつかのタブに分かれていて、
初期表示の「Properties」タブには、
選択中の要素のプロパティ一覧が表示されます。
表示されるだけでなく、
ダブルクリックすると値を変えることができます。
そして、なんと変えた値はリアルタイムで
実行中のアプリケーションに反映されます。


えっ、それぐらいなら
VisualStudioでもできるような…
たしかにそうですが、
VisualStudioのライブビジュアルツリーは、
謎のオーバーレイがアプリに出てしまったり、
いちいちVisualStudioが前面にでてしまったりして、
とにかく使いづらいです。
また、Snoop最大のメリットとして、
「デバッグ実行時じゃなくても使える」
というものがあります。
つまり、アプリケーションのユーザーのもとで
おかしな見た目になってしまっているとき、
Snoopさえ実行できれば、
原因調査をその場で行えるのです。
もちろん、開発中に、
思い通りになっていない見た目の要素があったら、
直接値を書き換えて、良い感じの値を探ることができます。
それでは、Snoopの活用方法について、
具体的に見ていきましょう。
活用方法その1。見た目や配置の調整
すでに上記でも説明しましたが、
Snoopはパッと見た目を変更して
良い感じの値を確認したい時に役に立ちます。
VisualStudioと違い
それ自体はシンプルな1つのウィンドウなので、
変にVisualStudioが前面に出てきてしまって邪魔、
といったことがありません。
↓の図のように、
1画面に両方出すこともそれほど難しくありません。
(Snoopのウィンドウが常に最前面なのはいただけませんが)

基本的にはxamlのエディットコンティニューでも事足りますが、
デュアルスクリーンでもない限り、
Snoopの方が視線移動しやすいですね。

職場ではデュアルスクリーンなこともあって、
最近はこの使い方は減りました。
でもこのブログを書くにあたっては超便利
ちなみに、プロパティは名前でフィルタリングできます。

また、「|」でいわゆる、or検索が行えます。

- Height|Width
- Margin|Padding
- Vertical|Horizontal
あたりは特に便利です。
活用方法その2。バインディングエラーの調査
また、Snoopはバインディングエラーの調査にも
役立てることができます。
例えば、次のように、
ComboBoxにバインディングしているはずの値が、
なぜか表示されないことがあったとします。

調査をしたいので、
おもむろにSnoopを当てて、
このComboBoxに「Ctrl」+「Shift」で
フォーカスさせます。
すると、何やら赤背景のプロパティがあります。
これは、BindingErrorが起きていることを示しています。

すかさずその行を右クリック→「Display Binding Errors」
を実行すると、エラーの中身が表示されます。


あとは、このエラーの内容から、
どうしてバインディングエラーが起きているのかを
調査していきます。
どうやら、「PiyoSelection」というプロパティが
「MainWindow」クラスに見つからずにエラーになっているようです。
また、実はツリー上部のフィルタリングのところから、
バインディングエラーになっているコントロールだけを
絞り込むことができます。

選択すると、この通りです。

バインディングエラーは、
実はデバッグ出力にも表示されているのですが、
ログが流れてしまったときなどは、
Snoopで直接覗いてみた方が調査が捗ります。
活用方法その他。DataContextタブやEventsタブの紹介
ここまででも十二分に便利ですが、
ほかのタブも非常に強力な機能が揃っています。
DataContextタブでDataContextのプロパティを閲覧・変更する
DataContextタブでは、
現在選択中の要素のDataContextが常に表示されています。

Propertiesタブと同様に、
DataContextの各プロパティを閲覧・変更することができます。
Eventsタブで発生したイベントと誰がハンドリングしたかを見る
Eventsタブでは、
WPFの規定するほとんどのイベントの購読が
記録されています。

これにより、
どのイベントがどんな順番で
発生するのかを視覚的に
確認することができます。
また、「+」を押して展開すると、
「そのイベントをハンドルした要素」
が閲覧できるため、
「誰にイベントを吸われてしまっているのか」
を調査する時に非常に役立ちます。

特にPreviewMouseDownなど、
Preview系のイベントをハンドルする
独自コントロールなどがいるときには、
「何が原因でマウスクリックが効かないのか分からない」
こともありうるのですが、
Snoopを使うと犯人探しがとても楽です。
さらにその他のタブは、さんさめ自身あまり便利に使えていない
さらにその他のタブは、
- 選択要素の任意のメソッドが呼び出せたり
- PowerShellを実行できたり
などと、
強力過ぎて逆に使いどころが良く分からないので
割愛します。
かなり古い記事ですが、
PowerShellの実用例が紹介されているページがありましたので、
興味のある方は読んでみると良いのではないでしょうか。
まとめ
まとめです。
- SnoopというWPFの強力な開発ツールを紹介
- ビジュアルツリーの可視化やプロパティを変更可能
- バインディングエラーの調査にも役立つ
- より高度なこともできるので色々触ってみると
デバッグ時の手札が増えるかも
最後までお読みいただきありがとうございました。
コメント