mthoutai

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

         在Windows编程中,我们已经习惯了一个窗体从创建到显示并获得焦点。

我们总感觉一个窗体创建出来获得焦点是理所理所当然的。一个窗体仅仅要显示就必须获得焦点。一个新窗体显示时。会收到到WM_SETFOCUS消息。然后旧的窗体会收到WM_KILLFOCUS消息。可能我们并不关心焦点的切换,由于从视觉角度来看,没有不论什么影响,可是键盘类的消息却在焦点变化时不断切换响应窗体。我们都知道,鼠标消息到来时,决定哪个窗体响应的是鼠标的坐标点。指哪里打哪里。键盘消息到来时,是谁来决定这个消息由哪个窗体响应呢?答案就是焦点。焦点是键盘消息的“舞台”,不论什么一个窗体仅仅要登上焦点这个“舞台”,全部的键盘类消息自然会分配到该窗体的窗体过程函数。

         只是话又说回来了,在什么情况下的窗体不须要获得焦点呢?我总结了一下。有两种情况:1.提示类窗体,像我们常见的tooltipWindow,酷狗的音乐列表上的提示窗体,还有我们每天都在使用的QQ,把鼠标移到你的头像上,出现的提示信息窗体,当QQ收到消息时,鼠标指到QQ的任务栏图标上时出现的消息列表提示窗体,等等。当然,你有可能会问了,提示窗体和焦点有什么关系,提示窗体获得焦点又如何?事实上表面上影响不是非常大,可是细致想想这是不符合原生的Window控件机制的,WM_MOUSEMOVE消息仅仅是个过客,键盘消息的“舞台”不能受鼠标移动干扰。比方我们打开QQ正在聊天时,或者正在填写一个比較繁琐的表格时。就晃了晃鼠标,光标没了。是不是非常蛋疼?我们还得又一次找到原来的编辑区,点一下刚才输入的位置让光标又一次出现才干继续输入。提示类窗体的创建和销毁往往是由WM_MOUSEMOVE来控制。所以呢这类窗体不能获得焦点。2.菜单窗体,Windows原生菜单是不获取焦点的,可能你还不信,事实上測试一下非常easy就能看出来。用Visual Studio建一个带有菜单的Win32项目。在窗体过程函数中捕获WM_KILLFOCUS消息。当菜单弹出时,WM_KILLFOCUS是不会响应的。

这里肯定你还会问,菜单的弹出和焦点有毛关系,谁规定了菜单必须是无焦点的?当然没有人规定这些。可是遵循原生的机制绝对是没错的。首先,在自己使用窗体模拟菜单时,焦点的切换是一个非常大的障碍,当然。有人还利用了这个特性,用WM_KILLFOCUS消息来控制菜单的窗体的销毁,这点在仅仅有一级的菜单中还能体现出优势,可是在做级联菜单时,问题又来了。子菜单出来。主菜单就会失去焦点。这无形其中增添了不少麻烦。各级菜单的耦合度变高,维护起来相当复杂。其次,焦点的不断切换会影响美观,我一提这个,肯定还会有不少人产生疑问。这从何说起啊?焦点在菜单上看不见摸不着,怎么能影响外观?之前有人用Duilib写过一个MenuDemo,应该非常多人都在用,效果看起来也不错。可是我个人认为还是有问题。可能大家也不会把这个当成问题。当鼠标在主菜单上滑行时,子菜单会轮流弹出,假设这时碰巧你在这个窗体的某个编辑里开启输入法输完东西时,输入法的工具框还保留当前窗体,因为子菜单的切换显示。会导致输入法的工具框不断闪烁。我个人感觉是影响美观,不知道大家的看法。原因是因为焦点的切换。导致输入法消息的响应窗体不断切换。从而导致其闪烁。

酷我音乐盒应该大家都用过。假设没猜错的话,用的就是Duilib的MenuDemo来实现的菜单。

你能够试一下。切换成中文在搜索框里输入几个字,然后右击弹出菜单,用鼠标在主菜单上滑行。让子菜单轮流弹出,看看那个输入法工具框是不是在不停闪烁。还有酷我音乐盒的全部提示框都是带焦点的。在菜单弹出时。不得不屏蔽某些控件的MouseHover事件,防止提示框的弹出抢走焦点。导致菜单销毁。

我想这应该是因为Duilib眼下不支持无焦点窗体的原因,为了解决问题。Skilla在Duilib的菜单上花了不少时间,因为网上这方面的资料真心太少了。还好最后有了成果,所以还有机会和大家一起分享。

       事实上,弄一个无焦点的窗体还是比較简单的。1.在窗体显示时,使用ShowWindow(SW_SHOWNOACTIVE),这样窗体显示时就不会获得焦点了。可是不能点击,点击完还是会获得焦点。 2.在窗体过程中截获WM_MOUSEACTIVATE消息,返回MA_NOACTIVE,这样就完美了,这时在客户区任凭你把鼠标左键右键全点烂了这个窗体也不会获得焦点。可是不能点标题栏,这也无妨,在Duilib中我们的窗体通常是不带原生标题栏的,把标题栏去掉就是了,这时仅仅要你不主动去SetFocus,窗体是永远不会获得焦点的。

       假设仅用Win32或者MFC编程,上面两步就够了。但我们还使用了Duilib框架,关于窗体焦点的控制參与的不不过CWindowWnd。CPaintManagerUI也做了大量的::SetFocus操作,假设不做处理窗体还是照样会获得焦点。

这时我们须要对源代码做一下小小的改动。1.给CPaintManagerUI加入一个布尔类型属性bool m_bUnfocusPaintWindow;来区分所绘制窗体是否为无焦点窗体,在构造时初始化为false。并加入Get、Set方法。让外界能訪问到。2.在CPaintManagerUI的cpp文件里查找全部的::SetFocus操作(注意前面的两个点),给全部的::SetFocus操作加上if(!m_bUnfocusPaintWindow)的推断。这时我们仅须要在将m_bUnfocusPaintWindow设置为true就可以防止CPaintManager抢走焦点。这样既不影响原来的功能,又达到了我们想要的效果。3.为了方便使用在CDialogBuilder的cpp文件的219行加上一个推断

else if(_tcscmp(pstrName, _T("unfocus")) == 0)
                    {
                        if(_tcscmp(pstrName,_T("true")))
                        pManager->SetUnfocusPaintWindow(true);
                    }

这样,我们就能够在xml里面设置窗体是否为无焦点窗体了,仅仅需在Window标签上加一个unfocus="true"的属性就能够了。比如

<Window size="400,240"  caption="0,0,0,40"  roundcorner="5,5" unfocus="true">

        这仅仅是无焦点窗体在TipWindow上的应用,关于菜单的实现我会尽快写好Demo来和大家一起分享。


        假设大家还有什么不明确的地方。或者对我的改动有什么意见或的法的,能够直接留言,或者联系QQ:848861075(Skilla)

posted on 2017-04-24 15:11  mthoutai  阅读(904)  评论(0编辑  收藏  举报