[WPF 自定义控件]让Form在加载后自动获得焦点
1. 需求
加载后让第一个输入框或者焦点是个很基本的功能,典型的如“登录”对话框。一般来说“登录”对话框加载后“用户名”应该马上获得焦点,用户只需输入用户名,点击Tab,再输入密码,点击回车就完成了登录操作。
在WPF中要让一个控件在加载时获得焦点应该很简单,只需要在Loaded事件后调用Focus()就行了。但有时表单是动态添加的,或者第一个表单元素会根据某些条件显示或隐藏,这时很难简单地让第一个控件获得焦点。
为了实现这个功能我创建了一个叫FocusService的工具类,这篇文章介绍这个类的使用及原理,以及补充一些WPF焦点的知识。
2. 实现
public static readonly DependencyProperty IsAutoFocusProperty =
DependencyProperty.RegisterAttached("IsAutoFocus", typeof(bool), typeof(FocusService), new PropertyMetadata(default(bool), OnIsAutoFocusChanged));
public static bool GetIsAutoFocus(DependencyObject obj) => (bool)obj.GetValue(IsAutoFocusProperty);
public static void SetIsAutoFocus(DependencyObject obj, bool value) => obj.SetValue(IsAutoFocusProperty, value);
private static void OnIsAutoFocusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (oldValue == newValue)
{
return;
}
if (obj is FrameworkElement target)
{
target.Loaded -= OnTargetLoaded;
if (newValue)
{
target.Loaded += OnTargetLoaded;
}
}
}
private static void OnTargetLoaded(object sender, RoutedEventArgs e)
{
var element = sender as FrameworkElement;
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(element))
return;
var request = new TraversalRequest(FocusNavigationDirection.Next);
element.MoveFocus(request);
}
上面是FocusService的代码,它使用IsAutoFocus这个附加属性控制是否自动获得焦点,做成附加属性是为了可在XAML上控制。这个附加属性不仅可以用在Control上,还可以用在Grid等其它UI元素上。在Form中是在DefaultStyle设用Setter设置了默认值,以前提过一般情况下附加属性和依赖属性都不会在代码里设置默认值。
<Setter Property="local:FocusService.IsAutoFocus"
Value="True" />
MoveFocus
在FrameworkElement上将IsAutoFocus附加属性设置为True的话(False不处理),这个FrameworkElement会在Loaded事件调用MoveFocus函数将键盘焦点移动到自身VisualTree中第一个可以接受焦点的元素上。大致上,MoveFocus的具体操作是使用深度优先的方式遍历VisualTree,找到第一个IsTabStob、Focusable和IsVisible都为True的元素并调用Keyboard.Focus函数。所谓的“第一个”,基本上和用户直觉上理解的一致。
DesignerProperties.GetIsInDesignMode
DesignerProperties.GetIsInDesignMode方法用于确定元素是否运行在设计器中。VisualStudio的设计器太过强大,几乎是所见即所得,大部分代码都可以在设计视图里运行。OnTargetLoaded里判断如果是运行在设计器就不执行后面的操作,是避免每次刷新设计视图都让它获得焦点。
VisualStudio的设计器真的十分强大,但有时又会因为程序的数据没准备好或各种原因而报错,如果遇到设计器的错误又不想处理具体原因可以考虑简单粗暴地使用DesignerProperties.GetIsInDesignMode判断并直接return。
3. 两种焦点类型
作为补充知识,这篇文章将简单介绍一下WPF的焦点。
3.1 键盘焦点
键盘焦点指当前正在接收键盘输入的UI元素。 在整个桌面上,只能有一个具有键盘焦点的元素。为了使UI元素可以获得焦点,它的Focusable和IsVisible必须为True。通常,对于非控件类Focusable属性值的默认值为False。
Keyboard类可以用于处理键盘焦点,代码如下:
Keyboard.Focus(FirstTextBox);
Focus函数如果执行成功,UI元素的IsKeyboardFocused将被设置为True,并且它本身或VisualTree上各级父元素的IsKeyboardFocusWithin都会变成True。
当然,如果UI元素并未加载到VisualTree上Focus函数不会执行成功,所以通常在Loaded事件以后才执行Focus函数。
3.2 逻辑焦点
逻辑焦点是指FocusScope中的FocusManager.FocusedElement,一个应用程序中可以有多个获得逻辑焦点的元素,但只有一个获得键盘焦点的元素。获得键盘焦点的元素同时也获得逻辑焦点。
FocusScope
FocusScope可以通过FocusManager.IsFocusScope改变。
<StackPanel Name="focusScope1"
FocusManager.IsFocusScope="True"
Height="200" Width="200">
<Button Name="button1" Height="50" Width="50"/>
<Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
FocusedElement
FocusManager还用于管理逻辑焦点,它使用GetFocusedElement(DependencyObject)获取FocusScope中获得逻辑焦点的元素,使用SetFocusedElement(DependencyObject, IInputElement)将元素设置为逻辑焦点。
3.3 Window的逻辑焦点
Window默认为FocusScope,它在静态构造函数中将IsFocusScope设置为True(不在DefaultStyle中设置):
FocusManager.IsFocusScopeProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata(true));
在Window加载(或者Window本身被激活)时,它都会用类似的代码让Window中的逻辑焦点元素获得焦点。
DependencyObject doContent = Content as DependencyObject;
if (doContent != null)
{
IInputElement focusedElement = FocusManager.GetFocusedElement(doContent) as IInputElement;
if (focusedElement != null)
focusedElement.Focus();
}
4. 结语
其实没有这个类也可以,反正代码简单,只是想通过这个类介绍下附加属性和Focus的用法。
做自定义控件要做好焦点管理,尤其是现在,因为很多设计师、产品经理、开发者都有丰富的手机应用开发设计经验,由于手机上的键盘导航逻辑和桌面应用的有些出入,所以键盘导航的细节很容易被忽视。
不过,通常来说用着用着觉得不顺手就会有人提出需求,细心的开发者总会渐渐把键盘导航做好。
5. 参考
FocusManager Class (System.Windows.Input) Microsoft Docs
Keyboard.Focus(IInputElement) Method (System.Windows.Input) Microsoft Docs
UIElement.MoveFocus(TraversalRequest) Method (System.Windows) Microsoft Docs
6. 源码
Kino.Toolkit.Wpf_FocusService.cs
[WPF 自定义控件]让Form在加载后自动获得焦点的更多相关文章
- [WPF自定义控件库] 让Form在加载后自动获得焦点
原文:[WPF自定义控件库] 让Form在加载后自动获得焦点 1. 需求 加载后让第一个输入框或者焦点是个很基本的功能,典型的如"登录"对话框.一般来说"登录" ...
- jquery-事件之页面框架加载后自动执行
jQuery事件之页面框架加载后自动执行 1)概述 HTML执行是按自上而下编译,而<script>一般写在body结束之前.如果在HTML加载的过程中卡住, 比如加载图片等,没有显示出来 ...
- Angular页面加载后自动弹窗
首先在控制器内写好一个弹窗,我用的是ionic的默认提示对话框 // 一个确认对话框 $scope.showConfirm = function() { var confirmPopup = $ion ...
- 存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。刷新 ObjectStateManager 项。
在用asp.net MVC3 的MusicStore时候 Edit某个数据项时提示下面的错误: 存储区更新.插入或删除语句影响到了意外的行数(0).实体在加载后可能被修改或删除.刷新 ObjectSt ...
- unobtrusive验证,ajax局部加载后验证失效解决方法
页面加载后运行此代码 $(function() {$.validator.unobtrusive.parse($("form")); }); 原因: 页面加载后unobtrusiv ...
- javascript设置网页刷新或者重新加载后滚动条的位置不变
有个同事说再javascript中你可以做任何你想做的事情,当时觉得不以为然,今天遇到个问题,就是页面重新加载后总是回到页面的顶部,如果客户只想看到他想看到的部分是怎么变化的,这个体验就好了.原本想象 ...
- [Asp.net mvc]实体更新异常:存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。
学习asp.net mvc 时在更新实体进行SaveChanges()的时候出现了异常,异常如下: “/”应用程序中的服务器错误. 存储区更新.插入或删除语句影响到了意外的行数(0).实体在加载后可能 ...
- 关于NGUI的动态加载后的刷新显示问题,解决办法!!
http://momowing.diandian.com/post/2012-09-06/40038001275 最近碰NGUI用到它的动态列表功能(ps:就是加东西,删除东西).我这里用的是UIDr ...
- uiwebview 加载html时字体变小 加载前或加载后改变字体大小
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #6122ae } p.p2 { margin: 0.0px 0. ...
随机推荐
- 【开发必备】2018最新中国国内可用API合集
中国国内可用API合集 笔记 OneNote - OneNote支持获取,复制,创建,更新,导入与导出笔记,支持为笔记添加多媒体内容,管理权限等.提供SDK和Demo. 为知笔记 - 为知笔记Wind ...
- 基于Tomcat的GeoServer部署步骤
一.安装JAVA 资源:JDK1.8 提取码:0y26 步骤: 1.安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置": 2. ...
- Flutter报错记录
1.Could not find an option named "androidx". Run 'flutter -h' (or 'flutter -h') for avai ...
- Docker设置镜像加速
一.为什么要设置镜像加速 由于docker的镜像源地址再国外,例如官方地址:https://hub.docker.com/search?q=hyperledger&type=image:因此下 ...
- React劲爆新特性Hooks 重构去哪儿网火车票PWA
React劲爆新特性Hooks 重构去哪儿网火车票PWA 获取课程资料链接:点击这里获取 本课程先带你细数最近一年来React的新特性,如Hooks.Redux API,让你从头理解Hooks对传统R ...
- javascript学习总结之函数
前言 在学习javascript函数的时候,有几个经常很容易混淆的方法,call,apply,bind,caller,callee,这些方法的使用,这些也可以说是会频繁使用的一些方法,在此通过查阅相关 ...
- js实现常见排序算法
电脑配置 CPU:AMD X4 640 内存: 宏想 DDR3 1600MHz 8g 主板:华擎 980DE3/U3S3 R2.0 浏览器:chrome 79.0.3945.88(正式版本) (64 ...
- 【BZOJ 2138】stone
Problem Description 话说 \(Nan\) 在海边等人,预计还要等上 \(M\) 分钟.为了打发时间,他玩起了石子. \(Nan\) 搬来了 \(N\) 堆石子,编号为 \(1\) ...
- CAS与ABA问题产生和优雅解决
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
- 中国古风唯美水墨工作计划汇报PPT模板推荐
模版来源:http://ppt.dede58.com/