WPF三大基础元素:键盘、鼠标与焦点

键盘类与事件处理

  • WPF框架中内置了System.Input.Keyboard基础键盘类,该类提供了丰富的键盘相关功能,包括描述键盘状态的属性、处理键盘操作的方法以及一系列事件。这些键盘事件不仅直接由Keyboard类提供,还通过UIElement等XAML基元素类向外传递。
  • 处理键盘输入时,常用的两个事件组是:
  1. KeyDown和PreviewKeyDown事件:当键盘键按下时,这两个事件会被触发KeyDown事件属于冒泡路由事件,这意味着它会从底层元素向上传播到包含它的元素。而PreviewKeyDown事件隧道路由事件,它会将事件沿着逻辑树向上传递,直到它被处理或路由到根元素
  2. KeyUp和PreviewKeyUp事件:当键盘键释放时,这两个事件会被触发KeyUp事件同样是冒泡路由事件,而PreviewKeyUp事件则是隧道路由事件
  • 如果要使某个UI元素能够接收并响应键盘输入,首要条件是该元素必须具有焦点。大部分UIElement的派生类默认都可获取焦点,但是像StackPanel和Canvas这类Panel类,默认情况下其Focusable属性被设置为false,因此不能直接获取焦点。若需此类元素能响应键盘输入,则需要显式将其Focusable属性设为true
  • 下面写个以PreviewKeyDown为例子:
<Window x:Class="WpfKeyboardEventsApp.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfKeyboardEventsApp"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"  Focusable="True" PreviewKeyDown="Window_PreviewKeyDown"><Grid><!--添加TextBlock用来显示键盘输入显示效果--><TextBlock x:Name="myTextBlock" Text="这里演示“静音”、“增大音量”、“减小音量”这三个快捷键" Foreground="White" Background="Green" FontSize="16"  Height="33" Padding="10,6"  HorizontalAlignment="Center"></TextBlock></Grid>
</Window>
using System.Windows;
using System.Windows.Input;namespace WpfKeyboardEventsApp
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>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的事件处理程序来处理按键事件。效果如下:
PreviewKeyDown

鼠标类与鼠标事件处理

  • WPF框架内建的System.Input.Mouse类为开发人员提供了丰富的鼠标相关功能,涵盖了关于鼠标状态的各种事件、方法及属性。类似于键盘事件的处理方式,Mouse类所包含的这些事件同样通过UIElement和其他XAML基元素类传递给应用程序。
  • 鼠标事件主要分为以下几个类别,每个类别都有对应的冒泡路由事件和隧道路由事件对:
  1. MouseDown和PreviewMouseDown事件:当用户按下鼠标按钮时触发,用于响应鼠标键按下动作。
  2. MouseUp和PreviewMouseUp事件:在用户释放鼠标按钮时发生,用于处理鼠标键抬起的动作。
  3. MouseEnter和PreviewMouseEnter事件:当鼠标光标进入控件区域时通知,标志着鼠标从外部移入控件。
  4. MouseLeave和PreviewMouseLeave事件:当鼠标离开控件边界时激活,表明鼠标从控件内部移至外部区域。
  5. MouseMove和PreviewMouseMove事件:随着鼠标在控件范围内移动而持续触发,可用于实时追踪鼠标的移动轨迹。
    要对于获取精确的鼠标位置,可通过调用Mouse类的GetPosition静态方法,该方法接受一个UIElement参数,返回的是相对于指定控件坐标系内的鼠标位置坐标,从而使得开发者能够根据具体控件上下文来捕获并处理鼠标的位置变化。
  • 下面写个例子:
<Grid><Grid.RowDefinitions><RowDefinition></RowDefinition><RowDefinition></RowDefinition><RowDefinition></RowDefinition></Grid.RowDefinitions><!--添加一个矩形来演示鼠标的相关操作--><Rectangle x:Name="myRectangle" Grid.Row="0"  Canvas.Left="246" Canvas.Top="46" Height="118"  Stroke="Black"  Width="200" Fill="White"MouseEnter="myRectangle_MouseEnter" MouseLeave="myRectangle_MouseLeave" MouseMove="myRectangle_MouseMove" MouseDown="myRectangle_MouseDown"MouseWheel="myRectangle_MouseWheel"></Rectangle><!--添加TextBlock来显示相关提示--><TextBlock x:Name="myTextBlock" Grid.Row="1" HorizontalAlignment="Center" FontSize="16" Foreground="Green" Text="鼠标滚动会改变矩形的宽度"></TextBlock></Grid>
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;namespace WpfKeyboardEventsApp
{/// <summary>/// MouseWindow.xaml 的交互逻辑/// </summary>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--;}}}
}

上述代码,包含一个矩形和一个文本块。当用户在矩形上执行鼠标操作时,将调用相应的事件处理程序来处理这些操作。文本块用于显示相关提示信息,效果图如下:
Mouse描述

WPF中的焦点处理机制

  • 在WPF应用程序中,焦点处理涉及到两个关键概念:键盘焦点和逻辑焦点。其中,键盘焦点特指当前接收键盘输入的元素,而逻辑焦点则是在特定焦点范围内的焦点元素。

1.键盘焦点

  • 是当前接受键盘输入的对象,整个桌面同一时间只能有一个元素拥有键盘焦点。在WPF中,若一个元素具有键盘焦点,其IsKeyboardFocused属性将被设置为true。通过Keyboard类的静态属性FocusedElement可以获取当前拥有键盘焦点的元素。
    若要使某个UI元素能够获取键盘焦点,需要确保该元素的Focusable属性和IsVisible属性均设为true。例如,默认情况下,Panel等基类的Focusable属性为false,因此需手动设置为true以允许此类元素获取焦点。
    用户可以通过交互操作(如按Tab键切换或点击元素)来改变键盘焦点。此外,还可以使用Keyboard.Focus(element)方法以编程方式尝试将键盘焦点赋予指定元素,但返回的结果可能是当前真正获得键盘焦点的元素,因为如果有其他因素阻止了请求,则结果可能与预期不符。
  • 下面来写个例子:
<StackPanel HorizontalAlignment="Center"><!--设置Focusable为True 表示可以用键盘获取到焦点--><TextBox x:Name="myTextBox" Focusable="True" Text="我是TextBox1" Margin="0,10"  GotKeyboardFocus="myTextBox_GotKeyboardFocus"/><TextBox x:Name="myTextBox2" Focusable="True" Text="我是TextBox2" Margin="0,10" GotKeyboardFocus="myTextBox_GotKeyboardFocus"/><TextBlock x:Name="myTextBlock" Text="我是一个提示,用来提示谁获取焦点获取焦点" Margin="0,10" Foreground="Red"/>
</StackPanel>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;namespace WpfKeyboardEventsApp
{/// <summary>/// FocusWindow.xaml 的交互逻辑/// </summary>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切换,文本显示对应的切换提示,效果图如下:
键盘焦点

2.逻辑焦点

  • 体现在FocusManager.FocusedElement上,它指的是在一个焦点范围内的焦点元素。当键盘焦点离开焦点范围时,尽管该元素失去了键盘焦点,但仍保留逻辑焦点。当键盘焦点重新回到焦点范围内时,该元素会再次获得键盘焦点,这使得键盘焦点能在多个焦点范围间切换,同时保证焦点返回时,焦点范围内的焦点元素能重获键盘焦点。
    -在一个应用程序中,可以存在多个拥有逻辑焦点的元素,但在单一焦点范围内仅有一个元素拥有逻辑焦点。可通过调用FocusManager.GetFocusScope(element)得到指定元素所在的焦点范围,而FocusManager.GetFocusedElement(scope)用于获取指定焦点范围内的焦点元素,FocusManager.SetFocusedElement(scope, element)则用来设置焦点范围内的焦点元素,通常用于设定初始焦点。
  • 下面来写个例子:
<StackPanel HorizontalAlignment="Center"><!--设置TabControl 下面的内容TextBox可以获取到键盘焦点--><TabControl><TabItem Header="Tab导航一"><TextBox x:Name="tab1TextBox" Text="我是Tab1的内容,我这里仅仅之作演示" Focusable="True" Foreground="Red"  Height="200"/></TabItem><TabItem Header="Tab导航二"><TextBox x:Name="tab2TextBox" Focusable="True" Text="我是Tab2的内容,我这里仅仅之作演示" Foreground="Blue"  Height="200"/></TabItem></TabControl>
</StackPanel>

上述代码,在一个具有多个TabItem的TabControl中,每个TabItem可以视为一个独立的焦点范围。当切换到另一个TabItem时,即使当前TabItem中的元素失去了键盘焦点,它仍保留逻辑焦点。若返回原来的TabItem,先前获得焦点的元素会重新获取键盘焦点,效果图如下:
逻辑焦点

3.键盘导航

  • 当用户按下导航键(如Tab、Shift+Tab、Ctrl+Tab组合键以及方向键)时,KeyboardNavigation类负责实现默认的键盘焦点导航行为。通过设置附加属性KeyboardNavigation.TabNavigation、ControlTabNavigation和DirectionalNavigation,可以自定义导航容器的导航策略,这些属性可取值包括Continue(继续导航。当键盘导航到达元素的末尾时,导航将继续到下一个元素)、Local(本地循环。当键盘导航到达元素的末尾时,导航将从头开始导航元素。)、Contained(包含循环。当键盘导航到达元素的末尾时,导航将从头开始导航元素,直到所有元素都被导航。)、Cycle(循环。当键盘导航到达元素的末尾时,导航将从头开始导航元素,直到所有元素都被导航。)、Once(一次。当键盘导航到达元素的末尾时,导航将停止。)和None(无。当键盘导航到达元素的末尾时,导航将不执行任何操作。),默认为Continue
  • 下面来写个例子:
<StackPanel HorizontalAlignment="Center"><!--设置按下Tab键时仅在其内部循环移动焦点,直到所有的都被导航停止--><ListBox KeyboardNavigation.TabNavigation="Contained"><ListBoxItem Content="我是ListBox选项1"/><ListBoxItem Content="我是ListBox选项2"/><ListBoxItem Content="我是ListBox选项3"/><ListBoxItem Content="我是ListBox选项4"/><ListBoxItem Content="我是ListBox选项5"/><ListBoxItem Content="我是ListBox选项6"/><ListBoxItem Content="我是ListBox选项7"/><ListBoxItem Content="我是ListBox选项8"/></ListBox>
</StackPanel>

上述代码,ListBox在按下Tab键时仅在其内部循环移动焦点,不跳转到其他控件上,到最后一个元素导航,效果图如下:
定义导航

4.焦点事件

  • 关于键盘焦点的事件包括PreviewGotKeyboardFocus、GotKeyboardFocus、PreviewLostKeyboardFocus以及LostKeyboardFocus。这些事件作为Keyboard类的附加事件,但在实际开发中更常作为基元素类上的路由事件使用。
    具体来说,当元素获取键盘焦点时触发GotKeyboardFocus事件;失去键盘焦点时引发LostKeyboardFocus事件。如果在预处理阶段即PreviewGotKeyboardFocus或 PreviewLostKeyboardFocus事件中设置了Handled为true,则焦点状态将不会发生改变。
  • 下面来写个例子:
<StackPanel HorizontalAlignment="Center"><!--设置TextBox添加三个事件--><TextBox x:Name="myTextBox" Focusable="True" PreviewGotKeyboardFocus="myTextBox_PreviewGotKeyboardFocus"  GotKeyboardFocus="myTextBox_GotKeyboardFocus"  LostKeyboardFocus="myTextBox_LostKeyboardFocus" Width="180" Margin="0,10"/><TextBox  Focusable="True" Text="我是用来测试焦点转移" Width="180" Margin="0,10"/><TextBlock x:Name="myTextBlock" Text="我是一个提示,用来提示焦点相关信息"  Foreground="Red" FontSize="16"/><Button Content="设置/取消阻止转移焦点" Click="Button_Click" Width="180"  Margin="0,30" Background="Green" Foreground="White" FontSize="16"></Button></StackPanel>
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;namespace WpfKeyboardEventsApp
{/// <summary>/// FocusWindow.xaml 的交互逻辑/// </summary>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获取或失去键盘焦点时,触发相应的事件,效果图如下:
焦点事件

公众号“点滴分享技术猿

关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/443291.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java框架面试篇

Spring框架 spring Bean线程安全问题 Scope注解 我们可以在bean的类上加Scope注解来声明这个Bean是单个实例还是多个实例。在默认情况下Bean是单个实例的&#xff0c;此时的注解中的属性默认为Scope("singleton")&#xff0c;Scope("prototype")则是一…

5G阅信是如何进行识别与解析的?5G阅信短链有什么用?

什么叫5G阅信&#xff1f;5G阅信&#xff0c;是通过短信网关向手机用户发送一条带有智能短信解析链接的文字短信&#xff0c;手机收到短信后&#xff0c;通过短信增强技术&#xff0c;自动从服务器拉取报备的信息&#xff0c;将短信内容解析为图文、视频、红包、卡券等样式的富…

如何在Raspberry Pi上启用SSH并结合cpolar内网穿透实现公网远程访问本地树莓派

文章目录 如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar4.2 cpolar进行token认证4.3 配置cpolar服务开机自启动4.4 查看映射到公网的隧道地址4.5 ssh公网…

如何缩小gif动图体积?一招教你在线压缩

GIF动图是一种常用的图像文件格式&#xff0c;它支持多帧动画和透明背景。与静态图像不同&#xff0c;GIF动图可以显示一系列的图像帧&#xff0c;以创建动态效果。这些动图通常以循环播放的方式展示&#xff0c;在社交媒体、聊天应用和网页上广泛使用。但是gif动图的体积通常也…

算法:积木游戏学习数学

一、算法描述 小华和小微一起通过玩积木游戏学习数学。 他们有很多积木&#xff0c;每个积木块上都有一个数字&#xff0c;积木块上的数字可能相同。 小华随机拿一些积木挨着排成一排&#xff0c;请小微找到这排积木中数字相同且所处位置最远的2块积木块&#xff0c;计算他们的…

我国个体工商户达1.24亿户,支撑近3亿人就业

官媒报道截至2023年底&#xff0c;全国登记在册个体工商户1.24亿户&#xff0c;占经营主体总量的67.4%&#xff0c;支撑近3亿人就业。 什么概念&#xff1f;我帮大家理解一下&#xff0c;2024年了&#xff0c;现在中国5个人里面就有一个人不用上班&#xff0c;而是自己当起了老…

k8s的operator基石:controller-runtime源码解析

写在之前 今天开始开更controller-runtime的源码阅读&#xff0c;笔者建议大家在阅读前了解以下知识&#xff0c;可能会帮助大家更好的理解源码逻辑。 1.client-go的基础使用 2. 使用kubebuilder搭建一个简单的controller-runtime环境 3.informer的基本思想 1.源码环境搭建 参…

【Matplotlib】科研绘图——折线图

文章目录 1、导入2、定义Font及Style3、设置图像大小及坐标刻度4、数据准备5、自定义draw6、其他设置7、效果图 1、导入 import matplotlib import matplotlib.pyplot as plt from matplotlib.backends.backend_pdf import PdfPages import numpy as np import pandas as pd %…

【Tomcat与网络8】从源码看Tomcat的层次结构

在前面我们介绍了如何通过源码来启动Tomcat&#xff0c;本文我们就来看一下Tomcat是如何一步步启动的&#xff0c;以及在启动过程中&#xff0c;不同的组件是如何加载的。 一般&#xff0c;我们可以通过 Tomcat 的 /bin 目录下的脚本 startup.sh 来启动 Tomcat&#xff0c;如果…

故障诊断 | 一文解决,CNN卷积神经网络故障诊断(Matlab)

文章目录 效果一览文章概述专栏介绍源码设计参考资料效果一览 文章概述 故障诊断 | 一文解决,CNN卷积神经网络故障诊断(Matlab) 专栏介绍 订阅【故障诊断】专栏,不定期更新机器学习和深度学习在故障诊断中的应用;订阅

微信开放平台第三方授权(第四篇)-wechat发送客服消息

1.发送客服消息 上一张介绍了发送消息需要用到的authorizer_access_token,和发送消息的接口结合使用&#xff0c;上面直接上代码。 重写WechatMpService 获取token&#xff0c;这个发消息会用到 package com.test.wechat.service;import com.test.wechat.config.WechatMpConf…

【百度Apollo】探索创新之路:深入了解Apollo开放平台

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…