我有一个带有 UserControl 的页面。如果用户在我想要处理的页面上的任何位置按 Esc。
我认为这就像连接 PreviewKeyDown 事件、测试 Esc 键然后处理它一样简单。但是,当我在事件处理程序中放置断点时,我发现它永远不会被调用。我想也许 UserControl 可能会被击中,所以我在那里尝试了 PreviewKeyDown ......结果相同。
有谁知道在 Page 对象上测试 KeyDown 或 PreviewKeyDown 的正确位置?
加载控件后,使用 附加到窗口的KeyDown
事件(或任何事件)Window.GetWindow(this)
,如下所示:
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
}
我曾经有过同样的问题。我有一个窗口,它承载一个框架,该框架加载/导航到各个页面,而这些页面又只包含 1 个包含正常控件/元素(标签、超链接等)的 UserControl。
我发现(当然经过几个小时的挫折!!!)是,如果您没有将注意力集中在上面提到的控件/元素之一上,则 PreviewKeyDown 事件不会在 UserControl 上触发(在 Frame 级别的某处停止)但是只要您将焦点(例如在 UserCotrol 的 Loaded 事件处理程序中调用 ctrl.Focus() )放在其中一个控件上,就会发生魔术,它就会起作用,事件也会为 UserControl 触发。
当然,事后想一想,这很有意义,但我 100% 肯定这会让 10 个人中至少有 5 人感到惊讶:) 疯狂的小东西叫做 WPF ......
干杯!
我相信 PreviewKeyDown 事件是一个隧道路由事件,而不是冒泡事件。如果是这种情况,那么如果您的 Page 没有收到该事件,则 UserControl 也不应该是,因为它位于可视树中的 Page 下方。也许尝试在您的应用程序的顶层(也许是窗口?)处理它,看看它是否正在接收事件?
另一个可能有帮助的选择是在 CodePlex 中使用诸如 Snoop之类的东西来确定事件的去向。
在我的 UserControl 上将 Focusable 属性设置为 true 为我解决了这个问题。
我提出了一种加强@Doc 提到的方法。
提到的以下代码@Doc 将起作用,因为 KeyDown 路由事件冒泡到最外层Window
。一旦Window
接收到从内部元素冒泡的 KeyDown 事件,Window
就会触发注册到它的任何 KeyDown 事件处理程序,如下所示HandleKeyPress
。
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
}
但这+=
是有风险的,程序员更有可能忘记取消注册事件处理程序。然后会发生内存泄漏或一些错误。
这里我建议,
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);
}
public void CallKeyDown(KeyEventArgs e) {
//Do your work
}
无需在 xaml 中编码。
什么对我有用:
仅在窗口Loaded 事件上监听 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!");
}
从 WinForms 调用 WPF 窗口时,我遇到了类似的问题。KeyDown或PreviewKeyDown事件均未触发。
var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
ElementHost.EnableModelessKeyboardInterop(wpfwindow);
wpfwindow.Show();
但是,将窗口显示为对话框,它可以工作
var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
ElementHost.EnableModelessKeyboardInterop(wpfwindow);
wpfwindow.ShowDialog();
PreviewKeyDown事件就像Escape和箭头键的魅力一样被触发。
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;
}
}
希望这有效。
您可以简单地使用该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";
}
}
不要忘记将Weindow_KeyDown
属性添加到 Window 标签
<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>
如果您不想附加到窗口的事件,请为您的用户控件或页面的可见更改添加一个事件:
IsVisibleChanged="Page_IsVisibleChanged"
然后在后面的代码中:
private void Page_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if(this.Visibility == Visibility.Visible)
{
this.Focusable = true;
this.Focus();
}
}
现在,如果您按任意键,您的事件 KeyDown 将被触发。
@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
}
}
这样,您可以在视图的不同部分拥有同一事件的不同句柄。句柄是本地的并且特定于控件,您无需将句柄添加到全局窗口。
尝试使用 ElementHost.EnableModelessKeyboardInterop 启用您的窗口。就我而言,它在 Keydown 事件中捕获箭头键。
我解决了这个问题,只是在视图中的一个按钮上调用 .Focus() 并确保窗口已完成所有元素的加载
这是经过测试并且绝对有效的。
Private Sub textbox1_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles textbox1_input.PreviewKeyDown
If (e.Key = Key.Down) Then
MessageBox.Show("It works.")
End If
End Sub
'detect key state directly with something like this below
Dim x As KeyStates = System.Windows.Input.Keyboard.GetKeyStates(Key.Down)
PreviewKeyDown 是大多数人错过的。将 PreviewKeyDown 视为键盘事件的整体观察者,(但如果我没记错,则不会影响它们)并且 KeyDown 事件正在您当前所在的控件或类的范围内被监听。