
WPFでは、フックを掛けたり、ひどいウィンピーに触れたりする必要はありません。実際、WPFを超えることはありません。 そもそも、 イベントをルーティングしたことを思い出してください。 イベントにサブスクライブできます。 原則として、これがタスクを実現するために知る必要があるすべてです:)
では、何をログに記録したいのでしょうか? キーボード、マウス、フォーカスの変更。 このため、UIElementクラスには次のイベントがあります。 次に、それらをサブスクライブする必要があります。
EventManager.RegisterClassHandler( typeof(UIElement), UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(MouseDown), true );
他のイベントを購読する
EventManager.RegisterClassHandler( typeof(UIElement), UIElement.PreviewMouseUpEvent, new MouseButtonEventHandler(MouseUp), true ); EventManager.RegisterClassHandler( typeof(UIElement), UIElement.PreviewKeyDownEvent, new KeyEventHandler(KeyDown), true ); EventManager.RegisterClassHandler( typeof(UIElement), UIElement.PreviewKeyUpEvent, new KeyEventHandler(KeyUp), true ); EventManager.RegisterClassHandler( typeof(UIElement), UIElement.PreviewTextInputEvent, new TextCompositionEventHandler(TextInput), true ); EventManager.RegisterClassHandler( typeof(UIElement), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnKeyboardFocusChanged), true ); EventManager.RegisterClassHandler( typeof(UIElement), Keyboard.LostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnKeyboardFocusChanged), true );
ここでの主なことは、これらすべてのイベントのハンドラーを作成し、どのボタンがクリックされたのか、誰から、何回クリックされたのかに関するデータを収集することです。 さて、猫をよく見てみましょう。

さて、コードを本当に見たい場合は、下のブロックを開いてこれを行うことができます
多くのコード
これらのイベントのハンドラーの作成を始めましょう。 すべてのイベントに共通の情報を収集するメソッドから始めましょう:このイベントを送信した要素の名前とタイプ:
FrameworkElementにNameプロパティが表示されるため、ソースとしてこのタイプのオブジェクトを受け入れます。
次に、マウスイベントを処理します。これらのイベントでは、押されたキーとダブルクリックであるかどうかに関する情報を収集します。
キーボードイベントでは、キーを収集します。 ただし、入力したパスワードを誤って削除したくないので、パスワードを入力する場合に、入力がKey値をKey.Multiplyに置き換える場所を理解したいと思います。 AutomationPeer.IsPasswordメソッドを使用して確認できます。 また、別のニュアンスは、ナビゲーションキーを押すときにそのような置換を行うことは意味がありません。これらは確かにパスワードの一部ではなく、他のアクションの開始点になる可能性があるためです。 たとえば、Tabキーを押してフォーカスを変更します。 その結果、次のものが得られます。
TextInputに進みましょう。 ここでは、原則として、すべてが簡単です。入力されたテキストを収集し、パスワードを忘れないでください。
まあ、最後に、焦点は残った:
Dictionary<string, string> CollectCommonProperties(FrameworkElement source) { Dictionary<string, string> properties = new Dictionary<string, string>(); properties["Name"] = source.Name; properties["ClassName"] = source.GetType().ToString(); return properties; }
FrameworkElementにNameプロパティが表示されるため、ソースとしてこのタイプのオブジェクトを受け入れます。
次に、マウスイベントを処理します。これらのイベントでは、押されたキーとダブルクリックであるかどうかに関する情報を収集します。
void MouseDown(object sender, MouseButtonEventArgs e) { FrameworkElement source = sender as FrameworkElement; if(source == null) return; var properties = CollectCommonProperties(source); LogMouse(properties, e, isUp: false); } void MouseUp(object sender, MouseButtonEventArgs e) { FrameworkElement source = sender as FrameworkElement; if(source == null) return; var properties = CollectCommonProperties(source); LogMouse(properties, e, isUp: true); } void LogMouse(IDictionary<string, string> properties, MouseButtonEventArgs e, bool isUp) { properties["mouseButton"] = e.ChangedButton.ToString(); properties["ClickCount"] = e.ClickCount.ToString(); Breadcrumb item = new Breadcrumb(); if(e.ClickCount == 2) { properties["action"] = "doubleClick"; item.Event = BreadcrumbEvent.MouseDoubleClick; } else if(isUp) { properties["action"] = "up"; item.Event = BreadcrumbEvent.MouseUp; } else { properties["action"] = "down"; item.Event = BreadcrumbEvent.MouseDown; } item.CustomData = properties; AddBreadcrumb(item); }
キーボードイベントでは、キーを収集します。 ただし、入力したパスワードを誤って削除したくないので、パスワードを入力する場合に、入力がKey値をKey.Multiplyに置き換える場所を理解したいと思います。 AutomationPeer.IsPasswordメソッドを使用して確認できます。 また、別のニュアンスは、ナビゲーションキーを押すときにそのような置換を行うことは意味がありません。これらは確かにパスワードの一部ではなく、他のアクションの開始点になる可能性があるためです。 たとえば、Tabキーを押してフォーカスを変更します。 その結果、次のものが得られます。
void KeyDown(object sender, KeyEventArgs e) { FrameworkElement source = sender as FrameworkElement; if(source == null) return; var properties = CollectCommonProperties(source); LogKeyboard(properties, e.Key, isUp: false, isPassword: CheckPasswordElement(e.OriginalSource as UIElement)); } void KeyUp(object sender, KeyEventArgs e) { FrameworkElement source = sender as FrameworkElement; if(source == null) return; var properties = CollectCommonProperties(source); LogKeyboard(properties, e.Key, isUp: true, isPassword: CheckPasswordElement(e.OriginalSource as UIElement)); } void LogKeyboard(IDictionary<string, string> properties, Key key, bool isUp, bool isPassword) { properties["key"] = GetKeyValue(key, isPassword).ToString(); properties["action"] = isUp ? "up" : "down"; Breadcrumb item = new Breadcrumb(); item.Event = isUp ? BreadcrumbEvent.KeyUp : BreadcrumbEvent.KeyDown; item.CustomData = properties; AddBreadcrumb(item); } Key GetKeyValue(Key key, bool isPassword) { if(!isPassword) return key; switch(key) { case Key.Tab: case Key.Left: case Key.Right: case Key.Up: case Key.Down: case Key.PageUp: case Key.PageDown: case Key.LeftCtrl: case Key.RightCtrl: case Key.LeftShift: case Key.RightShift: case Key.Enter: case Key.Home: case Key.End: return key; default: return Key.Multiply; } } bool CheckPasswordElement(UIElement targetElement) { if(targetElement != null) { AutomationPeer automationPeer = GetAutomationPeer(targetElement); return (automationPeer != null) ? automationPeer.IsPassword() : false; } return false; }
TextInputに進みましょう。 ここでは、原則として、すべてが簡単です。入力されたテキストを収集し、パスワードを忘れないでください。
void TextInput(object sender, TextCompositionEventArgs e) { FrameworkElement source = sender as FrameworkElement; if(source == null) return; var properties = CollectCommonProperties(source); LogTextInput(properties, e, CheckPasswordElement(e.OriginalSource as UIElement)); } void LogTextInput(IDictionary<string, string> properties, TextCompositionEventArgs e, bool isPassword) { properties["text"] = isPassword ? "*" : e.Text; properties["action"] = "press"; Breadcrumb item = new Breadcrumb(); item.Event = BreadcrumbEvent.KeyPress; item.CustomData = properties; AddBreadcrumb(item); }
まあ、最後に、焦点は残った:
void OnKeyboardFocusChanged(object sender, KeyboardFocusChangedEventArgs e) { FrameworkElement oldFocus = e.OldFocus as FrameworkElement; if(oldFocus != null) { var properties = CollectCommonProperties(oldFocus); LogFocus(properties, isGotFocus: false); } FrameworkElement newFocus = e.NewFocus as FrameworkElement; if(newFocus != null) { var properties = CollectCommonProperties(newFocus); LogFocus(properties, isGotFocus: true); } } void LogFocus(IDictionary<string, string> properties, bool isGotFocus) { Breadcrumb item = new Breadcrumb(); item.Event = isGotFocus ? BreadcrumbEvent.GotFocus : BreadcrumbEvent.LostFocus; item.CustomData = properties; AddBreadcrumb(item); }
ハンドラーの準備ができました。テストの時間です。 このための簡単なアプリケーションを作成し、Logifyを追加して行きましょう。

実行し、テキストボックスにqと入力し、[例外をスロー]をクリックしてアプリケーションをドロップし、収集した内容を確認します。 恐怖と恐怖が判明したので、ネタバレの下でそれを取り除いた。 本当にこれを見たい場合は、以下をクリックしてください。
非常に大きなログ

Eeee ...このようなことを考えたと思う

それはまさに私が思ったものです:)
何が間違っているのか、そしてなぜこのようなあいまいなメッセージの足跡を手に入れたのかを理解しましょう。
私が最初にしがみついているのは、2つの要素の間を歩くことに焦点を当てたイベントの集まりです。 さらに、これらのメッセージの量はログの総量のほぼ半分です。 実際には、実際にはフォーカスが一度変更されましたが、サブスクライブしているツリーの各要素からこの変更に関する通知を受け取ります。 まあ、私たちは冗談ではありません。何度も繰り返す必要はありません。 小切手を入力しましょう:
IInputElement FocusedElement { get; set; } void OnKeyboardFocusChanged(object sender, KeyboardFocusChangedEventArgs e) { if(FocusedElement != e.NewFocus) { FrameworkElement oldFocus = FocusedElement as FrameworkElement; if(oldFocus != null) { var properties = CollectCommonProperties(oldFocus); LogFocus(properties, false); } FrameworkElement newFocus = e.NewFocus as FrameworkElement; if(newFocus != null) { var properties = CollectCommonProperties(newFocus); LogFocus(properties, true); } FocusedElement = e.NewFocus; } }
何が起こったのか見てみましょう:

ここでは、はるかに美しい:)
ルーティングされたイベントは要素のツリーを通過し、それぞれが私たちに通知するため、同じイベントに対して非常に多くのログがあることがわかります。 要素の小さなツリーがあり、ログにはすでにたくさんの穀物があります。 実際のアプリケーションではどうなりますか? 私も考えることを恐れています。 最初または最後を除いて、これらのログをすべて明示的に破棄することはできません。 十分な大きさのビジュアルツリーがある場合、特に要素の名前がない場合は、ウィンドウまたはTextBoxをクリックしたというメッセージによって何かが通知されることはほとんどありません。 しかし、このリストを短くして、イベントが発生した場所を読み、同時に理解できるようにするのが私たちの力です。
UIElementでイベントをサブスクライブしましたが、実際には、彼の相続人のほとんどからのメッセージを無視できます。 たとえば、BorderまたはTextBlockからのキーストロークの通知にはほとんど関心がありません。 これらの要素のほとんどはアクションに関与しません。 私にとっては、コントロールイベントをサブスクライブすることになると思われます。
EventManager.RegisterClassHandler( typeof(Control), UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(MouseDown), true );
その他のイベント
EventManager.RegisterClassHandler( typeof(Control), UIElement.PreviewMouseUpEvent, new MouseButtonEventHandler(MouseUp), true ); EventManager.RegisterClassHandler( typeof(Control), UIElement.PreviewKeyDownEvent, new KeyEventHandler(KeyDown), true ); EventManager.RegisterClassHandler( typeof(Control), UIElement.PreviewKeyUpEvent, new KeyEventHandler(KeyUp), true ); EventManager.RegisterClassHandler( typeof(Control), UIElement.PreviewTextInputEvent, new TextCompositionEventHandler(TextInput), true ); EventManager.RegisterClassHandler( typeof(Control), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnKeyboardFocusChanged), true ); EventManager.RegisterClassHandler( typeof(Control), Keyboard.LostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnKeyboardFocusChanged), true );
その結果、ログははるかに読みやすくなり、多数のイベントがある場合でも、それを見るのは怖くありません。

もちろん、完璧に制限はありません。このログをさらに読みやすくするための方法がいくつかあります。 これは次の記事の1つになります。