System.Input.Keyboard
基础键盘类
,该类提供了丰富的键盘相关功能,包括描述键盘状态
的属性、处理键盘操作
的方法以及一系列事件。这些键盘事件不仅直接由Keyboard类
提供,还通过UIElement等XAML基元素类
向外传递。处理键盘输入
时,常用的两个事件组
是:键盘键
被按下
时,这两个事件
会被触发
。KeyDown事件属于冒泡路由事件
,这意味着它会从底层元素向上传播
到包含它的元素
。而PreviewKeyDown事件是隧道路由事件
,它会将事件沿着逻辑树向上传递
,直到它被处理或路由到根元素
。键盘键
被释放
时,这两个事件
会被触发
。KeyUp事件同样是冒泡路由事件
,而PreviewKeyUp事件则是隧道路由事件
。UI元素
能够接收并响应
键盘输入,首要条件是该元素必须具有焦点
。大部分UIElement的派生类默认
都可获取焦点
,但是像StackPanel和Canvas这类Panel类,默认情况下其Focusable
属性被设置为false
,因此不能
直接获取焦点
。若需此类元素能响应键盘输入,则需要显式
将其Focusable
属性设为true
。PreviewKeyDown
为例子:
using System.Windows;
using System.Windows.Input;
namespace WpfKeyboardEventsApp
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.VolumeMute)
{
// 按下“静音”键
myTextBlock.Text = "按下“静音”键";
e.Handled = true;
}
else if (e.Key == Key.VolumeUp)
{
// 按下“增大音量”键
myTextBlock.Text = "按下“增大音量”键";
e.Handled = true;
}
else if (e.Key == Key.VolumeDown)
{
// 按下“减小音量”键
myTextBlock.Text = "按下“减小音量”键";
e.Handled = true;
}
}
}
}
上述代码,当用户在窗口中按下键盘按键时,将调用名为Window_PreviewKeyDown的事件处理程序来处理按键事件。效果如下:
System.Input.Mouse
类为开发人员提供了丰富的鼠标相关功能,涵盖了关于鼠标状态的各种事件、方法及属性。类似于键盘事件的处理方式,Mouse类所包含的这些事件同样通过UIElement和其他XAML基元素类传递给应用程序。按下鼠标
按钮时触发,用于响应鼠标键按下动作。释放鼠标
按钮时发生,用于处理鼠标键抬起的动作。鼠标光标进入
控件区域时通知,标志着鼠标从外部移入控件。鼠标离开
控件边界时激活,表明鼠标从控件内部移至外部区域。鼠标在控件范围内移动
而持续触发,可用于实时追踪鼠标的移动轨迹。精确的鼠标
位置,可通过调用Mouse类的GetPosition
静态方法,该方法接受一个UIElement参数,返回的是相对于指定控件坐标系内的鼠标位置坐标
,从而使得开发者能够根据具体控件上下文来捕获并处理鼠标的位置变化。
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfKeyboardEventsApp
{
///
/// MouseWindow.xaml 的交互逻辑
///
public partial class MouseWindow : Window
{
public MouseWindow()
{
InitializeComponent();
}
private void myRectangle_MouseEnter(object sender, MouseEventArgs e)
{
// 鼠标进入控件时,控件的颜色为蓝色
myRectangle.Fill = new SolidColorBrush(Colors.Blue);
}
private void myRectangle_MouseLeave(object sender, MouseEventArgs e)
{
// 鼠标离开控件时,控件的颜色为白色
myRectangle.Fill = new SolidColorBrush(Colors.White);
}
private void myRectangle_MouseMove(object sender, MouseEventArgs e)
{
// 获取基于Rectangle的鼠标的坐标
Point pointBaseRectangle = Mouse.GetPosition(myRectangle);
myTextBlock.Text = $"鼠标位置矩形的底部为 ({pointBaseRectangle.X},{pointBaseRectangle.Y})";
myTextBlock.Text += "\r\n";
// 获取基于窗体的鼠标的坐标
Point pointBaseWindow = Mouse.GetPosition(this);
myTextBlock.Text += $"鼠标位置基于窗口为 ({pointBaseWindow.X},{pointBaseWindow.Y})";
}
private void myRectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
// 获取点出的鼠标的按钮,是左边还是右边
MouseButton button = e.ChangedButton;
myTextBlock.Text += "\r\n";
myTextBlock.Text += $"鼠标按键是{button.ToString()}";
}
private void myRectangle_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
{
// 如果向上推动滚轮,图形的宽度增加
myRectangle.Width++;
}
if (e.Delta < 0)
{
// 如果向下推动滚轮,图形的宽度减小
myRectangle.Width--;
}
}
}
}
上述代码,包含一个矩形和一个文本块。当用户在矩形上执行鼠标操作时,将调用相应的事件处理程序来处理这些操作。文本块用于显示相关提示信息,效果图如下:
键盘焦点和逻辑焦点
。其中,键盘焦点
特指当前接收键盘输入
的元素,而逻辑焦点
则是在特定焦点范围内的焦点
元素。接受键盘
输入的对象,整个桌面同一时间只能有一个元素拥有键盘焦点
。在WPF中,若一个元素具有键盘焦点,其IsKeyboardFocused
属性将被设置为true
。通过Keyboard
类的静态属性FocusedElement
可以获取当前拥有键盘焦点的元素。Focusable
属性和IsVisible
属性均设为true
。例如,默认情况下,Panel等基类的Focusable属性为false,因此需手动设置为true以允许此类元素获取焦点。按Tab键切换或点击元素
)来改变键盘焦点。此外,还可以使用Keyboard.Focus(element)
方法以编程方式尝试将键盘焦点赋予指定元素,但返回的结果可能是当前真正获得键盘焦点的元素,因为如果有其他因素阻止了请求,则结果可能与预期不符。
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfKeyboardEventsApp
{
///
/// FocusWindow.xaml 的交互逻辑
///
public partial class FocusWindow : Window
{
public FocusWindow()
{
InitializeComponent();
// 初始化设置myTextBox为焦点
Keyboard.Focus(myTextBox);
}
private void myTextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
myTextBlock.Text = $"{((TextBox)sender).Name} 获取了键盘焦点";
}
}
}
上述代码,包含两个输入框和一个文本宽,默认设置第一个输入框为焦点,利用Tab切换,文本显示对应的切换提示,效果图如下:
FocusManager.FocusedElement
上,它指的是在一个焦点范围内
的焦点元素。当键盘焦点离开焦点范围时,尽管该元素失去了键盘焦点,但仍保留逻辑焦点。当键盘焦点重新回到焦点范围内时,该元素会再次获得键盘焦点,这使得键盘焦点能在多个焦点范围间切换,同时保证焦点返回时,焦点范围内的焦点元素能重获键盘焦点。FocusManager.GetFocusScope(element)
得到指定元素所在的焦点范围,而FocusManager.GetFocusedElement(scope)
用于获取指定焦点范围内的焦点元素,FocusManager.SetFocusedElement(scope, element)
则用来设置焦点范围内的焦点元素,通常用于设定初始焦点。
上述代码,在一个具有多个TabItem的TabControl中,每个TabItem可以视为一个独立的焦点范围。当切换到另一个TabItem时,即使当前TabItem中的元素失去了键盘焦点,它仍保留逻辑焦点。若返回原来的TabItem,先前获得焦点的元素会重新获取键盘焦点,效果图如下:
导航键
(如Tab、Shift+Tab、Ctrl+Tab组合键以及方向键
)时,KeyboardNavigation
类负责实现默认的键盘焦点导航行为。通过设置附加属性KeyboardNavigation.TabNavigation、ControlTabNavigation和DirectionalNavigation
,可以自定义导航容器的导航策略,这些属性可取值包括Continue
(继续导航。当键盘导航到达元素的末尾时,导航将继续到下一个元素)、Local
(本地循环。当键盘导航到达元素的末尾时,导航将从头开始导航元素。)、Contained
(包含循环。当键盘导航到达元素的末尾时,导航将从头开始导航元素,直到所有元素都被导航。)、Cycle
(循环。当键盘导航到达元素的末尾时,导航将从头开始导航元素,直到所有元素都被导航。)、Once
(一次。当键盘导航到达元素的末尾时,导航将停止。)和None(
无。当键盘导航到达元素的末尾时,导航将不执行任何操作。),默认为Continue
。
上述代码,ListBox在按下Tab键时仅在其内部循环移动焦点,不跳转到其他控件上,到最后一个元素导航,效果图如下:
PreviewGotKeyboardFocus、GotKeyboardFocus、PreviewLostKeyboardFocus以及LostKeyboardFocus
。这些事件作为Keyboard类
的附加事件,但在实际开发中更常作为基元素类上的路由事件使用。GotKeyboardFocus
事件;失去键盘焦点时引发LostKeyboardFocus
事件。如果在预处理阶段即PreviewGotKeyboardFocus或 PreviewLostKeyboardFocus
事件中设置了Handled为true
,则焦点状态将不会发生改变。
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfKeyboardEventsApp
{
///
/// FocusWindow.xaml 的交互逻辑
///
public partial class FocusWindow : Window
{
private bool disableFocus = false; //禁止转移焦点
public FocusWindow()
{
InitializeComponent();
// 初始化设置myTextBox为焦点
Keyboard.Focus(myTextBox);
}
private void myTextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
//获得焦点
//myTextBlock.Text = $"{((TextBox)sender).Name} 获取了键盘焦点";
myTextBlock.Text = $"我获得焦点了";
myTextBox.Background = Brushes.Yellow;
}
private void myTextBox_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
//焦点转移时触发
if (disableFocus)
{
myTextBlock.Text = $"我焦点转移阻止了";
e.Handled = true; // 阻止焦点转移
}
}
private void myTextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
//失去焦点
myTextBlock.Text = $"我失去焦点了";
myTextBox.Background = Brushes.White;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
disableFocus = !disableFocus;
}
}
}
上述代码,当TextBox获取或失去键盘焦点时,触发相应的事件,效果图如下:
公众号“点滴分享技术猿”