致微软:对WPF与Win32互操作焦点控制的Sample质疑

附件1:原MSDN的Sample  附件2:Helloj2ee修改的Sample

本文是讨论WPFWin32互操作的一个MSDNSample。这个Sample似乎有些问题。我写下来也是希望有各位高手能够指点。看看是微软不小心的小错误,还是我HelloJ2ee误打误撞。如果写这个Sample的微软同仁看到了这个例子,还请您多多指教。

 

我和芸芸众生一般,学习微软的技术,当然首当其冲的资料就是MSDN。学习WPFWin32

的互操作。我学习了微软的这个Sample,名为Hosting a Win32 HWND in WPF Sample。诸位可以在本地安装的MSDN里下载到这个例子,也可以在线上下载http://msdn.microsoft.com/zh-cn/library/ms771352.aspx 当然本文提供的第一个附件也是从这上面下载下来的。

这个例子能够很成功的将Win32嵌入在WPF当中。在焦点控制上,预期的结果应该是按下Tab键时。焦点会从111,如此反复循环。

 

如果你初次尝试,也许能够成功。百分之五十的可能性。为什么说是百分之五十呢,你可以再尝试着按一下Shift键。现在你的情况多半是从1开始到2 再到10,再2 10 焦点甚至无法从Win32窗口出去。如果你也是这种情况,那么到此为止。HelloJ2ee还掌握着真理。

 

那么这个示例的问题出在哪儿呢?初步来看问题如下,下面的代码是示例中重载了TranslateAccelerator的部分代码片断。问题出在第二行。这里原意是希望按下Shift键而且焦点在2号编辑框上,则让焦点回到1号按钮。但是它传递的是FocusNavigationDirection::Last

 

代码
if (GetKeyState(VK_SHIFT) && GetFocus() == firstTabStop) {
                    request 
= gcnew TraversalRequest(FocusNavigationDirection::Last);
                }
                
else if (!GetKeyState(VK_SHIFT) && GetFocus() == lastTabStop) {
                    request 
= gcnew TraversalRequest(FocusNavigationDirection::Next);
                }
                
if (request != nullptr) {
                    
return ((IKeyboardInputSink^)this)->KeyboardInputSite->OnNoMoreTabStops(request);
                }

 

 

 

我们再看下面的一个代码片断,这是示例当中的TabInfo的实现,看到FocusNavigationDirection::Last没有?它会将焦点设置到10Cancel按钮。于是就出现了前面我们说的情况从210 再从210这样反复循环下去。

 

代码
        virtual bool TabInto(TraversalRequest^ request) override {
            
if (request->FocusNavigationDirection == FocusNavigationDirection::Last) {
                HWND lastTabStop 
= GetDlgItem(dialog, IDCANCEL);
                SetFocus(lastTabStop);
            }
            
else {
                HWND firstTabStop 
= GetDlgItem(dialog, IDC_EDIT1);
                SetFocus(firstTabStop);
            }
            
return true;
        }

 

 

需要将前面的代码片断(重载了TranslateAccelerator的部分),

改变成FocusNavigationDirection::Previous,才对。但是这样也不会达到我们设想的预期效果。这一次更为糟糕,如果按一下Shift键。那么会出现这样的情况就是从12 再从21 如此反复循环。当然我说的这种情况,也许你不会出现。那是因为出现这样的情况是%50的可能性。%50的可能性就是在于你是否按下Shift键。

 

注意 我说的按下Shift 不是说一直按着不放 而是按了一下 甚至是无意就按了一下 整个焦点的控制就乱了。

这个程序的焦点控制的本意是不按Shift 只按Tab 焦点从111 这样循环下去。

而按Shift+Tab 焦点就从111 这样循环下去。现在所有的问题就在Shift键上。如果各位还和我一样出现这样的情况。那么真理还在Helloj2ee这边。

 

问题的关键在于GetKeyState(VK_SHIFT)。我在学习这个源码的时候,无意当中发现了这个源码作者的标注了一段话:

// this code should work, but therea bug with interop shift-tab in current builds

 if (GetKeyState(VK_SHIFT) && GetFocus() == firstTabStop) {

 

大致意思也就是说 这个地方Shift+Tab会有bug。那么正好和我发现的问题也是一样的。但是他说这个代码是可以工作的。只不过因为互操作的原因导致有bug。(英文不好不知道理解错否

 

那么我也信了,以为不需要再解决了。但是忍不住看了一下GetKeyState的文档。最关键的是GetKeyState的返回值得注解。如下:

The return value specifies the status of the specified virtual key, as follows:

If the high-order bit is 1, the key is down; otherwise, it is up.

If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled, and off when the key is untoggled.

 

诸位如果E文不好的好,我就勉为其难解释一下,大致的意思就是 这个返回值是一个Short型(两个Byte),那么高位如果是1的话,也就说明按键被按下。反之则没按下。如果低位是1 的话,证明按键是Toggled状态,反之则不是。随后举例说明什么是Toggled状态,比如大写开关,如果按下它就一直是大写。再按一次就恢复原状,就是一个开关一样的作用。

 

于是我又觉得隐约会有些问题。因为这个代码里只是通过判断GetKeyState(VK_SHIFT)返回值是否为0来表示是否按下。那么刚才一系列的症状是不是表示Shift处于Toggled状态呢?

 

于是我只好再将该代码做一次修改,修改后的重载的TranslateAccelerator代码片断如下,这里不是判断GetKeyState是否为0来判断Shift是否按下的。而是直接取其低位来判断Shift是否按下。然后再采取相应的动作。

 

代码
SHORT keystate = GetKeyState(VK_SHIFT);
                BYTE downstate 
= HIBYTE(keystate);
                BYTE togglestate 
= LOBYTE(keystate);
                
if(downstate)
                {
                    
if(GetFocus()==firstTabStop)
                    {
                        request 
= gcnew TraversalRequest(FocusNavigationDirection::Previous);
                    }
                }
                
else
                {
                    
if(GetFocus()==lastTabStop)
                    {
                        request 
= gcnew TraversalRequest(FocusNavigationDirection::Next);
                    }
                }

 

这样的改动下来,无论你是按Tab还是按Shift+Tab都能如你所愿的焦点移动。附件当中加上Helloj2ee的既是我改过的例子。

         这个示例是MSDN当中的一个示例,关于这个例子还有一篇相应的文档对该例子进行了说明,因此发现这个问题我首先是自省,看看是否是自己的错以免误伤了别人。这么晚在这儿发帖 也是把问题暴露出来。看看Helloj2ee是否一不小心还是自己搞错了。如果写这个Sample的微软同仁看到了这个例子,还请您多多指教。

posted @ 2009-12-31 01:47  helloj2ee  阅读(1885)  评论(11编辑  收藏  举报