SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  263 随笔 :: 19 文章 :: 3009 评论 :: 74万 阅读
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
本文中的例子源代码请到这里下载:Smartphone上用.NET CF截获Back键的演示程序

当然,这个程序本身并非一定要用截获Back键这样的方式来设计,甚至应该有更好的设计方式。这个例子并不时为了给大家演示Back键如何出问题的,而是为了演示如何截获Back键,并进行自己的逻辑设计。代码并不时非常完善,凑合着看看吧。

现在我们假设有这么一个任务:设计一个“实时”在线系统,为天文学家提供一个宇宙重大事件预报提醒功能。这些重大事件从发生到消失可能很短,也许只有一天的事件可以观察,所以希望不要因为不小心碰到哪一个按钮错过了这个事情。另外程序启动之后希望能够尽量避免窗体的创建和销毁,因为也许这个窗体上面有很多的控件,比较浪费时间。(这里并没有太多的控件)



根据这个任务的要求,我们可能希望这个窗口在按了Back键之后:
1、提醒天文学家这个现象很快就会结束,是否真的要等一会儿再来处理这件事情等等。
2、即使天文学家真的希望该窗口在他眼前消失,我们也不希望这个窗口被关闭。



实际上当你按下Back键之后,操作系统会给你的窗口发送WM_HOTKEY。在一个Form接受到这个消息之后,如果没有任何控件处理该消息,那就会把当前窗口的ZOrder改变到后台,这个是用VC2k5写SmartPhone程序测试的结果。也许.NET CF内部还有他的黑箱操作,比如会把这个消息翻译成WM_KEYPRESS,该消息会引发KeyPress事件,最终我们可以接受到一个Keys.Escape。如果我们这个时候把e.Handled设为true,系统默认行为就不会发生。但这个事件在.NET CF里面只会在有焦点的控件上面引发,这一点实际上在某种情况下是有一些缺陷的。就像当我们希望截获Back键这一个情况,如果我们期望通过KEYPRESS事件来得到,并进行处理,我们不得不为所有可能有焦点的空间都绑上委派。这个方法也许不是很美观,因为要么每一个new出来的form都通过一个函数进行遍历和绑定,要么每一个窗口类型都是派生自同一个窗口类型。无论如何,对于构造来说就可能会产生效率问题。而实际上操作系统只是想应用程序发消息,应用程序接到消息之后会向Form转发。Form接受到了HOTKEY消息之后,会查找是否有用有焦点的控件,有就会向他转发该消息。如果控件不表示处理该消息,则Form就会试图改变ZOrder。

很明显,如果我们截留了发送给Form的消息,则Form就不会做任何事情。当然我们也可以让程序进行判断,或者让用户来选择是否执行默认的行为。可是并不是将这个发送给Form的消息直接进行处理就完事了,如果那样的话我们会发现TextBox没有办法删除字符了。因此我们在接受到发送给Form的消息时,需要首先判断是否有用有焦点的控件,这些控件是否处理Back键。如果一个控件表示会处理KEYDOWN(0x1b Escape)这个消息,则继续处理HOTKEY消息,否则才进行你设计的其他逻辑。

为什么这里选择了KEYDOWN消息而不是HOTKEY呢?因为只有KEYDOWN和KEYPRESS消息才会返回“是否被处理”,选择KEYDOWN来进行测试也许副作用要少一点。那为什么不干脆直接发送一套KEYDOWN/KEYPRESS/KEYUP消息来模拟一个删除操作呢?在某些情况下发送backspace的keydown等消息是能够起到删除前一个字符的作用,看起来跟Back键的效果一样。但是在至少已种情况下会出现异常:正在用中文输入法进行输入的时候,比如刚输入了拼音“b”,发现不对想删掉修改成“p”,这个时候就会出现“异常情况”,具体如何大家可以自己试验一下。

Oh, wait! 上一篇文章里面不是说无法通过WndProc/MessageFilter来截获消息吗?没错,所以我们需要参照OpennetCf上面的ApplicationEx内部的代码。那一个ApplicationEx竟然实现了诸如MessageFilter的功能,说实话却是足够强大了,但是在我看来代价有点高昂。因为暂时来说我只是希望处理一下这个Back键的功能,为了这么简单的一件事情而需要装载整个OpennetCF得不偿失。所以我决定写这么一个简单的AppRunner来处理这一个问题。其实Application就是简单的通过PeekMessage/GetMessage/TranslateMessage/DispatchMessage等系统函数来维护这么一个消息循环的,我们自己来写一个这样的东西也并不困难。具体的代码我就不在这里罗嗦了,如果有什么疑问,欢迎讨论。
posted on   Sumtec  阅读(1333)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
点击右上角即可分享
微信分享提示