web-dev-qa-db-ja.com

WPF PageまたはUserControlオブジェクトでKeyDownイベントをキャプチャするにはどうすればよいですか?

UserControlのあるページがあります。ユーザーがページの任意の場所でEscキーを押した場合、処理します。

これは、PreviewKeyDownイベントをフックし、Escキーをテストして処理するのと同じくらい簡単だと思いました。ただし、イベントハンドラーにブレークポイントを配置したときに、呼び出されないことがわかりました。 UserControlがヒットするかもしれないと思ったので、そこでPreviewKeyDownを試してみました...同じ結果です。

PageオブジェクトでKeyDownまたはPreviewKeyDownをテストする適切な場所を知っている人はいますか?

34
Sailing Judo

PreviewKeyDownイベントはバブルイベントではなく、トンネリングルーティングイベントだと思います。その場合、Pageがイベントを取得していない場合、UserControlはビジュアルツリーのPageの下にあるため、UserControlもそうではありません。アプリの最上位(おそらくウィンドウ?)で処理してみて、イベントを取得しているかどうかを確認しますか?

役立つ可能性のある別のオプションは、 CodePlexのスヌープ のようなものを使用して、イベントがどこに向かっているのかを把握することです。

21
Andy

ウィンドウのイベントにアタッチします

コントロールがロードされたら、次のようにWindow.GetWindow(this)を使用して、WindowのKeyDownイベント(または任意のイベント)にアタッチします。

XAML

<UserControl Loaded="UserControl_Loaded">
</UserControl>

コードビハインド

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
  var window = Window.GetWindow(this);
  window.KeyDown += HandleKeyPress;
}

private void HandleKeyPress(object sender, KeyEventArgs e) {
  //Do work
}
36
Daniel

私は同じ問題を抱えていました。私は、通常のコントロール/要素(ラベル、ハイパーリンクなど)を含む1つのUserControlのみを含むさまざまなページにロード/ナビゲートするフレームをホストするウィンドウを持っています。

私が発見したのは(もちろん数時間のフラストレーションの後!!!)、上記のコントロール/要素のいずれかに焦点を当てていない場合、PreviewKeyDownイベントはUserControlで発生しません(フレームレベルで停止します)がコントロールの1つにフォーカス(たとえば、UserCotrolのLoadedイベントハンドラー内でctrl.Focus()を呼び出す)が発生するとすぐに動作し、UserControlに対してもイベントが発生します。

もちろん、後でそれを考えるとこれは十分に理にかなっていますが、驚いたことに10人中5人は少なくとも5人が死ぬことになると確信しています。

乾杯!

26
el_bibiq

上記の@Docを強化する方法を提案します。

次の@Docのコードは、KeyDownルーティングイベントが最も外側のWindowにバブルされるため機能します。 Windowが内部要素からバブル化されたKeyDownイベントを受け取ると、Windowは、このHandleKeyPressのように、それに登録されたKeyDownイベントハンドラーをトリガーします。

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
  var window = Window.GetWindow(this);
  window.KeyDown += HandleKeyPress;
}

private void HandleKeyPress(object sender, KeyEventArgs e) {
  //Do work
}

しかしこれは +=は危険です。プログラマはイベントハンドラの登録を忘れる可能性が高くなります。その後、メモリリークまたはいくつかのバグが発生します。

ここで提案します、

YourWindow.xaml.cs

protected override void OnKeyDown(KeyEventArgs e)
{
    base.OnKeyDown(e);

    // You need to have a reference to YourUserControlViewModel in the class.
    YourUserControlViewModel.CallKeyDown(e);

    // Or, if you don't like ViewModel, hold your user-control in the class then
    YourUserControl.CallKeyDown(e);
}

YourUserControlViewModel.csまたはYourUserControl.xaml.cs

public void CallKeyDown(KeyEventArgs e) {
  //Do your work
}

Xamlでコーディングする必要はありません。

9
Jeff T.

私のために正確に働いたもの:

ウィンドウ上のみLoaded event PreviewKeyDownイベントをリッスンします:

void GameScreen_Loaded(object sender, RoutedEventArgs e)
{
     this.PreviewKeyDown += GameScreen_PreviewKeyDown;
     this.Focusable = true;
     this.Focus();
}

void GameScreen_PreviewKeyDown(object sender, KeyEventArgs e)
{
     MessageBox.Show("it works!");   
}
3
alansiqueira27

WinFormsからWPFウィンドウを呼び出すときに、同様の問題が発生しました。 KeyDownまたはPreviewKeyDownイベントは発生しませんでした。

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.Show();

しかし、ウィンドウをdialogとして表示すると、うまくいきました

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.ShowDialog();

PreviewKeyDownEscapeおよび矢印キー。

void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Escape:
                    this.Close();
                    break;
                case Key.Right:
                    page_forward();
                    break;
                case Key.Left:
                    page_backward();
                    break;
            }
        }

これがうまくいくことを願っています。

1
Janez Krnc

@Danielのソリューションの代わりに、ウィンドウ全体にイベントハンドラーを追加する必要はありませんが、以下を実行します。

public partial class MyControl : UserControl{

public MyControl()
{
   MouseEnter+= MouseEnterHandler;
   MouseLeave+= MouseLeaveHandler;
}

protected void MouseEnterHandler(object sender, MouseEventArgs e)
{
   var view = sender as MyControl;
   view.KeyDown += HandleKeyPress;
   view.KeyUp += HandleKeyReleased;
   view.Focus();
}

protected void MouseLeaveHandler(object sender, MouseEventArgs e)
{
   var view = sender as MyControl;
   view.KeyDown -= HandleKeyPress;
   view.KeyUp -= HandleKeyReleased;
}

protected void HandleKeyPress(object sender, KeyEventArgs e)
{
    // What happens on key pressed
}

protected void HandleKeyReleased(object sender, KeyEventArgs e)
{
    // What happens on key released
}

}

このようにして、ビューの異なる部分で同じイベントの異なるハンドルを持つことができます。ハンドルはローカルであり、コントロールに固有です。グローバルウィンドウにハンドルを追加しないでください。

0
Leo Kolezhuk

単にWeindow_KeyDownイベントを使用できます。

ここに例があります

private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            // ... Test for F1 key.
            if (e.Key == Key.F1)
            {
                this.Title = "You pressed F1 key";
            }
        }

windowタグにWeindow_KeyDown属性を追加することを忘れないでください

<Window x:Class="WpfApplication25.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        KeyDown="Window_KeyDown">
</Window>
0
dE Source

ウィンドウのイベントに添付したくない場合は、UserControlまたはPageの変更を表示するためのイベントを追加します。

IsVisibleChanged="Page_IsVisibleChanged"

次に、コードビハインドで:

private void Page_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if(this.Visibility == Visibility.Visible)
            {
                this.Focusable = true;
                this.Focus();
            }
        }

これで、キーを押すとイベントKeyDownが発生します。

0
Kevin

ElementHost.EnableModelessKeyboardInteropでウィンドウを有効にしてみてください。私の場合、Keydownイベントで矢印キーをキャプチャします。

0
user340104