Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来

如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来

作者头像
walterlv
发布于 2023-10-22 02:20:41
发布于 2023-10-22 02:20:41
6590
举报

我们有很多的调试工具可以帮助我们查看 WPF 窗口中当前获得键盘焦点的元素。本文介绍监控当前键盘焦点元素的方法,并且提供一个不需要任何调试工具的自己绘制键盘焦点元素的方法。


使用调试工具查看当前获得键盘焦点的元素

Visual Studio 带有实时可视化树的功能,使用此功能调试 WPF 程序的 UI 非常方便。

在打开实时可视化树后,我们可以略微认识一下这里的几个常用按钮:

这里,我们需要打开两个按钮:

  • 为当前选中的元素显示外框
  • 追踪具有焦点的元素

这样,只要你的应用程序当前获得焦点的元素发生了变化,就会有一个表示这个元素所在位置和边距的叠加层显示在窗口之上。

你可能已经注意到了,Visual Studio 附带的这一叠加层会导致鼠标无法穿透操作真正具有焦点的元素。这显然不能让这一功能一直打开使用,这是非常不方便的。

使用代码查看当前获得键盘焦点的元素

我们打算在代码中编写追踪焦点的逻辑。这可以规避 Visual Studio 中叠加层中的一些问题,同时还可以在任何环境下使用,而不用担心有没有装 Visual Studio。

获取当前获得键盘焦点的元素:

1

var focusedElement = Keyboard.FocusedElement;

不过只是拿到这个值并没有多少意义,我们需要:

  1. 能够实时刷新这个值;
  2. 能够将这个控件在界面上显示出来。

实时刷新

Keyboard路由事件可以监听,得知元素已获得键盘焦点。

1

Keyboard.AddGotKeyboardFocusHandler(xxx, OnGotFocus);

这里的 xxx 需要替换成监听键盘焦点的根元素。实际上,对于窗口来说,这个根元素可以唯一确定,就是窗口的根元素。于是我可以写一个辅助方法,用于找到这个窗口的根元素:

1 2 3 4 5 6 7 8 9

// 用于存储当前已经获取过的窗口根元素。 private FrameworkElement _root; // 获取当前窗口的根元素。 private FrameworkElement Root => _root ?? (_root = FindRootVisual(this)); // 一个辅助方法,用于根据某个元素为起点查找当前窗口的根元素。 private static FrameworkElement FindRootVisual(FrameworkElement source) => (FrameworkElement)((HwndSource)PresentationSource.FromVisual(source)).RootVisual;

于是,监听键盘焦点的代码就可以变成:

1 2 3 4 5 6 7 8 9

Keyboard.AddGotKeyboardFocusHandler(Root, OnGotFocus); void OnGotFocus(object sender, KeyboardFocusChangedEventArgs e) { if (e.NewFocus is FrameworkElement fe) { // 在这里可以输出或者显示这个获得了键盘焦点的元素。 } }

显示

为了显示一个跟踪焦点的控件,我写了一个 UserControl,里面的主要代码是:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

<Canvas IsHitTestVisible="False"> <Border x:Name="FocusBorder" BorderBrush="#80159f5c" BorderThickness="4" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" SnapsToDevicePixels="True"> <Border x:Name="OffsetBorder" Background="#80159f5c" Margin="-200 -4 -200 -4" Padding="12 0" HorizontalAlignment="Center" VerticalAlignment="Bottom" SnapsToDevicePixels="True"> <Border.RenderTransform> <TranslateTransform x:Name="OffsetTransform" Y="16" /> </Border.RenderTransform> <TextBlock x:Name="FocusDescriptionTextBlock" Foreground="White" HorizontalAlignment="Center" /> </Border> </Border> </Canvas>

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

using System; using System.Runtime.InteropServices; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Threading; namespace Walterlv.Windows { public partial class KeyboardFocusView : UserControl { public KeyboardFocusView() { InitializeComponent(); Loaded += OnLoaded; Unloaded += OnUnloaded; } private void OnLoaded(object sender, RoutedEventArgs e) { if (Keyboard.FocusedElement is FrameworkElement fe) { SetFocusVisual(fe); } Keyboard.AddGotKeyboardFocusHandler(Root, OnGotFocus); } private void OnUnloaded(object sender, RoutedEventArgs e) { Keyboard.RemoveGotKeyboardFocusHandler(Root, OnGotFocus); _root = null; } private void OnGotFocus(object sender, KeyboardFocusChangedEventArgs e) { if (e.NewFocus is FrameworkElement fe) { SetFocusVisual(fe); } } private void SetFocusVisual(FrameworkElement fe) { var topLeft = fe.TranslatePoint(new Point(), Root); var bottomRight = fe.TranslatePoint(new Point(fe.ActualWidth, fe.ActualHeight), Root); var isOnTop = topLeft.Y < 16; var isOnBottom = bottomRight.Y > Root.ActualHeight - 16; var bounds = new Rect(topLeft, bottomRight); Canvas.SetLeft(FocusBorder, bounds.X); Canvas.SetTop(FocusBorder, bounds.Y); FocusBorder.Width = bounds.Width; FocusBorder.Height = bounds.Height; FocusDescriptionTextBlock.Text = string.IsNullOrWhiteSpace(fe.Name) ? $"{fe.GetType().Name}" : $"{fe.Name}({fe.GetType().Name})"; } private FrameworkElement _root; private FrameworkElement Root => _root ?? (_root = FindRootVisual(this)); private static FrameworkElement FindRootVisual(FrameworkElement source) => (FrameworkElement)((HwndSource)PresentationSource.FromVisual(source)).RootVisual; } }

这样,只要将这个控件放到窗口中,这个控件就会一直跟踪窗口中的当前获得了键盘焦点的元素。当然,为了最好的显示效果,你需要将这个控件放到最顶层。

绘制并实时显示 WPF 程序中当前键盘焦点的元素

如果我们需要监听应用程序中所有窗口中的当前获得键盘焦点的元素怎么办呢?我们需要给所有当前激活的窗口监听 GotKeyboardFocus 事件。

于是,你需要我在另一篇博客中写的方法来监视整个 WPF 应用程序中的所有窗口:

里面有一段对 ApplicationWindowMonitor 类的使用:

1 2 3 4 5 6 7 8 9

var app = Application.Current; var monitor = new ApplicationWindowMonitor(app); monitor.ActiveWindowChanged += OnActiveWindowChanged; void OnActiveWindowChanged(object sender, ActiveWindowEventArgs e) { var newWindow = e.NewWindow; // 一旦有一个新的获得焦点的窗口出现,就可以在这里执行一些代码。 }

于是,我们只需要在 OnActiveWindowChanged 事件中,将我面前面写的控件 KeyboardFocusView 从原来的窗口中移除,然后放到新的窗口中即可监视新的窗口中的键盘焦点。

由于每一次的窗口激活状态的切换都会更新当前激活的窗口,所以,我们可以监听整个 WPF 应用程序中所有窗口中的键盘焦点。

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/how-to-track-wpf-focused-element.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-06-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
将 WPF 嵌入到 MFC 中,无法响应键盘输入
在 将 WPF 窗口嵌入到 MFC 窗口中 中提到,可以将 WPF 嵌入到 MFC 窗口中, 但遗留了一个没有发现的问题,WPF 界面,无法响应键盘的输入。
jgrass
2024/12/25
1520
准确判断一个 WPF 控件 / UI 元素当前是否显示在屏幕内
你的 WPF 窗口是可以拖到屏幕外面去的,所以拉几个元素到屏幕外很正常。你的屏幕可能有多个。你的多个屏幕可能有不同的 DPI。你检测的元素可能带有旋转。
walterlv
2023/10/22
8060
准确判断一个 WPF 控件 / UI 元素当前是否显示在屏幕内
实现一个 WPF 版本的 ConnectedAnimation
2017-12-25 11:44
walterlv
2018/09/18
6720
实现一个 WPF 版本的 ConnectedAnimation
[WPF自定义控件库] 让Form在加载后自动获得焦点
加载后让第一个输入框或者焦点是个很基本的功能,典型的如“登录”对话框。一般来说“登录”对话框加载后“用户名”应该马上获得焦点,用户只需输入用户名,点击Tab,再输入密码,点击回车就完成了登录操作。
dino.c
2019/06/03
1.7K0
wpf 自定义窗体 干货 干货
首先效果图如下: 步骤: 新建一个资源文件WindowsStyles.xaml 修改内容如下 : <ResourceDictionary xmlns="http://schemas.micr
zls365
2021/04/02
4960
WPF窗体中控件移动 + 拖拽大小 + 动画拖动
废话不多直接上菜 image.png 下载.gif /* 注意:只要不带焦点的控件包括用户控件 都可以拖动与拖拽大小 【基类中的【公共参数】可以自行修改哦】 使用方法[这是在一个窗体的后台代码]: //实例化对象 public DragControlsHelper dragControlsHelper = new DragControlsHelper(); //执行以下方法就可以拖拽了[this属于窗体的
Shunnet
2022/05/31
2K0
WPF窗体中控件移动 + 拖拽大小 + 动画拖动
WPF 自定义控件入门 Focusable 与焦点
自定义控件时,如果自定义的控件需要用来接收键盘消息或者是输入法的输入内容,那就需要关注到控件的焦点
林德熙
2023/04/07
2.1K0
WPF 自定义控件入门 Focusable 与焦点
[WPF 自定义控件]自定义一个“传统”的 Validation.ErrorTemplate
数据绑定模型允许您将与您Binding的对象相关联ValidationRules。 如果用户输入的值无效,你可能希望在应用程序 用户界面 (UI) 上提供一些有关错误的反馈。 提供此类反馈的一种方法是设置Validation.ErrorTemplate附加到自定义ControlTemplate的属性。
dino.c
2020/03/02
1.5K0
WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片
我们知道,在 WPF 中的坐标单位不是屏幕像素单位,所以如果需要知道某个控件的像素尺寸,以便做一些与屏幕像素尺寸相关的操作,就需要经过一些计算(例如得到屏幕的 DPI)。
walterlv
2023/10/22
7840
WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片
一点点从坑里爬出来:如何正确打开 WPF 里的 Popup?
在 WPF 中打开一个 Popup 并没有想象当中容易。虽说提供了一个 IsOpen 属性用于显示 Popup,但实际上造成的 Bug 会让你解得死去活来。Win32 的 WS_POPUP 也坑,不过 WPF 会额外再带来一些,所以本文只说 WPF。
walterlv
2023/10/22
6910
WPF 加载诡异的字体无法布局
如果在系统里面存在诡异的字体,同时自己的 WPF 中有一个控件尝试使用这个字体放在界面中,那么将会在界面布局过程炸了,整个控件或者整个界面布局都无法继续
林德熙
2021/03/16
1.4K0
WPF 程序如何移动焦点到其他控件
WPF 中可以使用 UIElement.Focus() 将焦点设置到某个特定的控件,也可以使用 TraversalRequest 仅仅移动焦点。本文介绍如何在 WPF 程序中控制控件的焦点。
walterlv
2023/10/22
6020
[WPF]使用WindowChrome自定义Window Style
做了WPF开发多年,一直未曾自己实现一个自定义Window Style,无论是《WPF编程宝典》或是各种博客都建议使用WindowStyle="None" 和 AllowsTransparency="True",于是想当然以为这样就可以了。最近来了兴致想自己实现一个,才知道WindowStyle="None" 的方式根本不好用,原因有几点:
dino.c
2019/01/18
2.3K0
[WPF]使用WindowChrome自定义Window Style
WPF 自定义文本框输入法 IME 跟随光标
本文非小白向,本文适合想开发自定义的文本框,从底层开始开发的文本库的伙伴。在开始之前,期望了解了文本库开发的基础知识
林德熙
2022/03/15
1.9K0
流畅设计 Fluent Design System 中的光照效果 RevealBrush,WPF 也能模拟实现啦!
发布于 2018-04-05 08:34 更新于 2018-05-29 12:56
walterlv
2018/09/18
8630
流畅设计 Fluent Design System 中的光照效果 RevealBrush,WPF 也能模拟实现啦!
采用WPF开发截图程序,so easy!
前言 QQ、微信截图功能已很强大了,似乎没必要在开发一个截图程序了。但是有时QQ热键就是被占用,不能快速的开启截屏;有时,天天挂着QQ,领导也不乐意。既然是程序员,就要自己开发截屏工具,功能随心所欲,岂不快哉。
梁规晓
2019/10/24
2.5K0
【NEW】WPF MVVM 模式下自写自用的窗口样式
SVG是一种图形文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形。它是基于XML(Extensible Markup Language),由World Wide Web Consortium(W3C)联盟进行开发的。严格来说应该是一种开放标准的矢量图形语言,可让你设计激动人心的、高分辨率的Web图形页面。用户可以直接用代码来描绘图像,可以用任何文字处理工具打开SVG图像,通过改变部分代码来使图像具有交互功能,并可以随时插入到HTML中通过浏览器来观看。
Shunnet
2022/09/01
2.4K0
【NEW】WPF MVVM 模式下自写自用的窗口样式
【愚公系列】2023年09月 WPF控件专题 Slider控件详解
WPF控件是Windows Presentation Foundation(WPF)中的基本用户界面元素。它们是可视化对象,可以用来创建各种用户界面。WPF控件可以分为两类:原生控件和自定义控件。
愚公搬代码
2023/09/29
1.2K2
如何监视 WPF 中的所有窗口,在所有窗口中订阅事件或者附加 UI
由于 WPF 路由事件(主要是隧道和冒泡)的存在,我们很容易能够通过只监听窗口中的某些事件使得整个窗口中所有控件发生的事件都被监听到。然而,如果我们希望监听的是整个应用程序中所有的事件呢?路由事件的路由可并不会跨越窗口边界呀?
walterlv
2023/10/22
6440
WPF 多线程 UI:设计一个异步加载 UI 的容器
2018-09-08 12:53
walterlv
2018/09/18
4.1K0
WPF 多线程 UI:设计一个异步加载 UI 的容器
推荐阅读
相关推荐
将 WPF 嵌入到 MFC 中,无法响应键盘输入
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档