| 
8.3 处理键盘消息 当用户操作键盘的时候下面的键盘消息就会发生: WM_KEYDOWN WM_KEYUP WM_CHAR 一个按键既可以按下也可以不按下。结果,处理键盘消息要比处理鼠标消息简单。击键不提供几何上可测量的数据。按键一般不像鼠标那样定义一个屏幕上的位置。因此,它们不能准确地告诉程序输入是为哪个控件准备的。这就是为什么要将焦点的概念应用于键盘消息的处理。基本上,拥有焦点的控件接受键盘消息。此外,在任何时刻,只能有结构中的一个控件获得焦点。后面的章节讨论了更多的关于焦点的细节。下面的代码展示了修正后的CXControl,增加了焦点的实现部分。 protected:     bool m_Focus;     // Focus control     CXControl * m_Focus; public:     // Called when the user presses a key     virtual void OnKeyDown(WPARAM Key, LPARAM Extended) = NULL;     // Called when the user releases a key     virtual void OnKeyUp(WPARAM Key, LPARAM Extended) = NULL;     // Get focus control     CXControl * GetFocus() {return m_Focus;}     // Set focus control     void SetFocus(CXControl * Control); }; 8.3.1 焦点  
 图8.6 拥有焦点的控件接收键盘消息。在任何时刻只能有一个控件拥有焦点。通过选择一个控件来把焦点转向它,换句话说就是,通过点击控件或者其他类似的过程来让控件获得焦点。从本质上来说就是,有一个控件获得焦点,就有一个控件失去它。下面的CXControl的成员方法展示了在层次中一直维持焦点是多么的奇妙以及焦点是如何从一个控件跳到另一个控件上去的。 void CXControl::SetFocus(CXControl * Control) {     if(!m_Focus)     {         if(GetParentControl())             GetParentControl->SetFocus(Control);         else         {             if(GetFocus())                 GetFocus()->m_Focus = false;             m_FocusControl = Control;             m_Focus = true;         }     } } 注意: 实际上,当一个控件的孩子控件获得焦点的时候你不需要向父控件(指它本身)通报。但是这里已经这么做了。这样,当有消息传递给结构中的顶层控件时,拥有焦点的控件就可以直接接收输入的信息。下一小节中会谈论到这些。 8.3.2 处理事件 在使用了焦点的概念之后,把键盘消息转化为键盘事件就成了一个十分简单的过程。你可以简单地调用拥有焦点的控件的键盘事件就可以了。现在PostMessage成员方法添加进了处理键盘消息的代码。 CXControl * CXControl: ostToAll(UINT msg, WPARAM wParam, LPARAM lParam, void * Data) {     switch(msg)     {         // Handle other messages here...     case WM_KEYUP:     case WM_KEYDOWN:         if(GetFocus())         {             if(msg == WM_KEYUP)                 GetFocus()->OnKeyUp(wParam, lParam);             if(msg == WM_KEYDOWN)                 GetFocus()->OnKeyDown(wParam, lParam);         }         break;     }     return NULL; }  8.4 处理控件绘制
 最后需要考虑的,同时也是最重要的事件之一就是绘制。每当结构接收到WM_RENDER消息时,所有的控件都应接收到OnRender事件。这是一个自定义的消息用来代替WM_PAINT。它看起来和下面的句子类似: #define WM_RENDER WM_USER+1 这个消息表示这个控件期待重绘自己,这个过程被称作是重绘。每个控件的外观都不一样,按钮一个样,而标签就是另外一个样子,等等。不同的控件如何绘制它们自己将在后面的章节中进行讨论。下面的代码是CXControl处理OnRender事件的一个实例。他演示了一个典型的控件如何使用CXTexture和CXPen类来绘制自己。后面的例子展示了绘制消息是如何通过结构传递给控件的。 bool CXTest::OnRender() {     D3DXVECTOR2 ControlAbsolutePos;     ControlAbsolutePos.x = 0;     ControlAbsolutePos.y = 0;     GetAbsolutePosition(&ControlAbsolutePos);     GetCanvas()->SetTranslation(&ControlAbsolutePos);     GetPen()->DrawTexture(GetCanvas());     GetCanvas()->SetTranslation(NULL); }  提示
 当窗口需要重绘的时候WndProc都会接收到WM_PAINT消息,尽管Direct3D在它自己的渲染程序中重绘窗口,但那是因为标准的窗口绘制太慢了。因此,WM_PAINT消息被一个自定义的WM_RENDER消息取代了,并且在Direct3D应用程序的渲染循环中手工传递到结构中去。如果你喜欢,你可以只是传递WM_PAINT消息,但是WM_RENDER更清楚、透明。 
 
  |