当前位置: 首页 > news >正文

一点点从坑里爬出来:如何正确打开 WPF 里的 Popup?

在 WPF 中打开一个 Popup 并没有想象当中容易。虽说提供了一个 IsOpen 属性用于显示 Popup,但实际上造成的 Bug 会让你解得死去活来。Win32 的 WS_POPUP 也坑,不过 WPF 会额外再带来一些,所以本文只说 WPF。


@TOC

先说结论

本文一开始就贴出打开一个 Popup 的代码

// 在以下代码中,我们假定 popup 是我们要显示出来的 Popup,而 textBox 是 Popup 中的文本框。
private async void WalterlvDemoControl_MouseUp(object sender, MouseButtonEventArgs e)
{
    // 必须延迟打开 Popup,如果在 MouseUp 中打开,会使得 Popup 无法获得焦点。
    await Task.Yield();
    popup.IsOpen = true;

    // 必须显式让 Popup 获得焦点,否则内部的 TextBox 输入时,IME 输入框无法跟随。
    await Task.Yield();
    var source = (HwndSource) PresentationSource.FromVisual(popup.Child);
    SetFocus(source.Handle);

    // 必须显式让文本框获得焦点(如果有的话)。
    await Task.Yield();
    Keyboard.Focus(textBox);
}

[DllImport("user32")]
public static extern IntPtr SetFocus(IntPtr hWnd);

如果你的 Popup 中没有文本框,那么最后的两段可以删除。

接下来一一说明。

不要在 MouseUp/Click 事件中打开 Popup

Popup 有一个属性 StaysOpen,当设置为 false 时,我们期待的效果是失焦后 Popup 关闭。然而如果你是在任何控件的 MouseUp 事件中打开的,那么 Popup 就不会获得焦点。既然不会获得焦点,那么也就不存在失焦的问题。

具体表现为,你打开了 Popup 后,Popup 不会自己再自动关闭了,除非你手动在 Popup 内部点一下让 Popup 获得焦点,随后才会自动关闭。

无论你在后面如何写让 Popup 以及内部控件获得焦点的代码,实际上这种情况下弹出的 Popup 不会真正获得焦点,除非手动点击。

所以我在以上代码中加上了 await Task.Yield() 这样可以让后续的代码不再在 MouseUp 事件中。

如果你的 Popup 中没有文本框,那么这样做就够了;如果有,那么还需要做后续处理。

需要显式为 Popup 设置焦点

注意注意,如果你的 Popup 中包含文本框,那么一定需要加上 SetFocus 的调用。WPF 版本的设置焦点,无论是逻辑焦点(xx.Focus())还是键盘焦点(Keyboard.Focus(xx))都无法真正让 Popup 获得焦点。这时打字,IME 框是不会跟随文本框的。

需要单独为 TextBox 再设置焦点

只是为 Popup 设置焦点的话,Popup 中的文本框没有获得焦点,是不能直接打字的。当然你可能需求如此。这里就没有特别说明的点了。


我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

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

相关文章:

  • Windows Linux 系统中获取端口被哪个应用程序占用
  • 设置用户无需密码自动登录到 Windows 系统
  • 最简单的代码,让 WPF 支持响应式布局
  • 当无边框窗口被子窗口遮挡导致难以调节窗口大小时,可通过处理 NCHITTEST 消息重新支持调节窗口大小
  • 如何给 GitHub Pages 配置多个域名?
  • 通过子类化窗口(SubClass)来为现有的某个窗口添加新的窗口处理程序(或者叫钩子,Hook)
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • git 乱改你的换行符?一句话设置让 git 不再碰你某个文件的换行符
  • Linux Shell 中需要转义的字符
  • Unity3D 入门:Unity Editor 编辑器常用快捷键
  • Unity3D 入门:Unity 项目版本管理建议使用的 .gitignore 忽略文件和 .gitattributes 文件(2020年4月更新)
  • Unity3D 入门:让 C# 脚本公开可在 Unity 编辑器中设置的属性
  • Unity3D 入门:如何管理 Unity 项目中的 NuGet 包?使用第三方 NuGet 包管理器——NuGetForUnity
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • Apache Pulsar 2.1 重磅发布
  • CSS魔法堂:Absolute Positioning就这个样
  • Docker: 容器互访的三种方式
  • Git学习与使用心得(1)—— 初始化
  • HTTP中GET与POST的区别 99%的错误认识
  • JavaScript HTML DOM
  • Joomla 2.x, 3.x useful code cheatsheet
  • LeetCode18.四数之和 JavaScript
  • Linux链接文件
  • node.js
  • ubuntu 下nginx安装 并支持https协议
  • Vue实战(四)登录/注册页的实现
  • 爱情 北京女病人
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 今年的LC3大会没了?
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 前端之Sass/Scss实战笔记
  • 什么是Javascript函数节流?
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 回归生活:清理微信公众号
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • 组复制官方翻译九、Group Replication Technical Details
  • ​一些不规范的GTID使用场景
  • # 数论-逆元
  • #define,static,const,三种常量的区别
  • #if 1...#endif
  • $GOPATH/go.mod exists but should not goland
  • (Python) SOAP Web Service (HTTP POST)
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • (转)重识new