随笔-250  评论-20  文章-55  trackbacks-0
被包含的窗口:
一个被包含的窗口是一个不响应任何消息的窗口,它将收到的所有消息重新发送到另外一个窗口的消息映射,这个另外的窗口就是它的容器窗口。通常情况下,被包含的窗口是它的容器窗口的子窗口,但情况并不是总是这样。容器窗口并不是必须等同于父窗口,包含与被包含的关系取决于C++类,被包含的窗口是容器窗口类的一个数据成员,而父窗口和子窗口的关系体现在屏幕上,它们的关系是创建窗口时确定的。

一个被包含的窗口建立在已注册的窗口类的基础之上,比如编辑框控件。如果一个编辑框被包含,那么发送到它的消息实际上被它的容器窗口的消息映射处理。使用这种方法,可以改变编辑框控件的标准行为。这有点类似于子类化但是不需要定义新类来子类化控件。和前面那个定义CnoNumEdit类响应WM_CHAR消息的例子相比,处理WM_CHAR消息的容器窗口类看起来如下:
class CMyWindow: public CWindowImpl
{
   CContainedWindow m_contained;
public:
   CMyWindow(): m_contained( _T("edit"), this, 99 )
   {
   }
   ...
CmyWindow是一个容器窗口类,它的构造函数对CcontainedWindow类型的成员做这样的初始化:被包含的窗口是编辑框,发送它的消息到“this”(它的父窗口),使用可选消息映射表99。
BEGIN_MSG_MAP( CMyWindow )
   MESSAGE_HANDLER( WM_CREATE, OnCreate )
   MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
ALT_MSG_MAP( 99 ) // contained window''s messages come here...
   MESSAGE_HANDLER( WM_CHAR, OnChar )
END_MSG_MAP()
当父窗口被创建的时候,被包含的窗口也被创建(在WM_CREATE消息的响应函数中)。因为被包含的控件是以编辑框为基础的,所以它在屏幕上看起来象一个编辑框:
LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& )
{
   RECT rc = { 10, 10, 200, 35 };
   m_contained.Create( *this, rc, _T("non-numeric edit"),
      WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 666 );
   return 0;
}
在这个例子中,容器窗口同时也是被包含窗口的父窗口。

当被包含的窗口收到WM_CHAR消息时,容器窗口的OnChar成员函数被调用。这个函数和前面的CnoNumEdit例子中的相同,但是在这个例子中,它时容器类的成员函数。
LRESULT OnChar( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )
   {
   TCHAR ch = wParam;
   if( _T(''0'') <= ch && ch <= _T(''9'') )
      MessageBeep( 0 );
   else
      bHandled = FALSE;
   return 0;
   }

LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& )
   {
   PostQuitMessage( 0 );
   return 0;
   }
};
我们同样也可以用被包含的窗口来子类化对话框中已经存在的控件,和正规的子类化不同,被子类化的窗口的消息时被容器窗口捕获的。在下面的例子中,一个对话框子类化了一个编辑框控件,把它转化成了被包含的窗口;那个对话框(容器)捕获WM_CHAR消息并忽略掉数字字符,然后在发送到编辑框控件。(CdialogImpl在ATL中的对话框类一节讲述。)
class CMyDialog: public CDialogImpl<CMyDialog>
{
public:
   enum { IDD = IDD_DIALOG1 };
// contained window is an edit control:
   CMyDialog(): m_contained( "edit", this, 123 )
   {
   }

   BEGIN_MSG_MAP( CMyDialog )
      MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
   ALT_MSG_MAP( 123 ) // contained window''s messages come here...
      MESSAGE_HANDLER( WM_CHAR, OnChar )
   END_MSG_MAP()

   LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& bHandled )
   {
   // when the dialog box is created, subclass its edit control:
      m_contained.SubclassWindow( GetDlgItem(IDC_EDIT1) );
      bHandled = FALSE;
      return 0;
   }

   LRESULT OnChar( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )
   {
      TCHAR ch = wParam;
      if( _T(''0'') <= ch && ch <= _T(''9'') )
         MessageBeep( 0 );
      else
         bHandled = FALSE;
      return 0;
   }

   CContainedWindow m_contained;
};
消息反射:
前面讲述了一些扩展窗口功能的方法,这些方法是通过使窗口响应发往窗口的消息实现的。和前面的方法相反,消息反射是使窗口能够响应从它们自己发出的消息。

当用户和控件交互的时候,控件通常使发送一个WM_COMMAND或者WM_NOTIFY消息给它的父窗口;然后父窗口做出响应,比如:
class CParentWindow: CWindowImpl<CParentWindow>
{
   // 假设这个窗口有一个按钮型的子窗口,
   // 并且其 ID 为 ID_BUTTON
   BEGIN_MSG_MAP( CParentWindow )
      COMMAND_ID_HANDLER( ID_BUTTON, OnButton )
      MESSAGE_HANDLER( WM_CTLCOLORBUTTON, OnColorButton )
      ...
当按钮被按下的时候,它发送一个命令消息给父窗口,然后CParentWindow::OnButton被调用。同理,当按钮需要被绘制的时候,它发送WM_CTLCOLORBUTTON消息给父窗口,CParentWindow::OnColorButton响应这个消息,它使用特定的画刷绘制控件。

某些情况下,让控件自己响应它发送出去的消息比让父窗口响应要好得多。ATL提供了消息反射的机制:当控件向父窗口发送消息的时候,父窗口能够将消息反射给控件。
class CParentWindow: CWindowImpl
{
   BEGIN_MSG_MAP( CParentWindow )
      MESSAGE_HANDLER( WM_CREATE, OnCreate )
      MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
      ...other messages that CParentWindow will handle...
      REFLECT_NOTIFICATIONS()
   END_MSG_MAP()
   ...
当父窗口收到一个消息,先查找它的消息映射表,如果没有和这个消息相匹配的入口,则REFLECT_NOTIFICATIONS宏使得该消息被反射给发送这个消息的控件。控件可以提供响应反射消息的处理函数,如下:
class CHandlesItsOwnMessages: CWindowImpl<CHandlesItsOwnMessage>
{
public:
   DECLARE_WND_SUPERCLASS( _T("Superbutton"), _T("button") )
   BEGIN_MSG_MAP( CHandlesItsOwnMessage )
      MESSAGE_HANDLER( OCM_COMMAND, OnCommand )
      MESSAGE_HANDLER( OCM_CTLCOLORBUTTON, OnColorButton )
      DEFAULT_REFLECTION_HANDLER()
   END_MSG_MAP()
   ...
注意,反射消息的消息标志以OCM_开头,而不是WM_。这可以让你区分这个消息究竟是否是被反射回来的。

这个控件要么是这个类的实例,要么是一个被子类化的按钮控件。例如:
// in CParentWindow:
   CHandlesItsOwnMessages m_button;
   LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& )
   {
      RECT rc; // initialize appropriately
      m_button.Create( *this, rc, _T("click me"), WS_CHILD|WS_VISIBLE );
      ...
或者,如果这个按钮控件是已存在的(例如,父窗口是一个对话框):
m_button.SubclassWindow( GetDlgItem(ID_BUTTON) );
下面的例子定义了一个CstaticLink类,它是一个Static控件,当点击它的时候,将打开一个指定的网页。所有从CstaticLink发送出去的消息都被它的父窗口反射回来(在这个例子中,用到对话框,请看ATL中的对话框类这一节)。除了响应反射回的命令消息,CstaticLink还处理反射回的WM_CTLCOLORSTATIC消息以便它能够让自己在点击前和点击后显示不同的颜色。
#include "stdafx.h"
#include "resource.h"

CComModule _Module;

class CStaticLink : public CWindowImpl<CStaticLink> {
/*
   Based on CStaticLink by Paul DiLascia, C++ Q&A, Microsoft Systems
   Journal 12/1997.
   Turns static controls into clickable "links" -- when the control is
   clicked, the file/program/webpage named in the control''s text (or
   set by SetLinkText()) is opened via ShellExecute().  Static control
   can be either text or graphic (bitmap, icon, etc.).
*/
public:
   DECLARE_WND_SUPERCLASS( _T("StaticLink"), _T("Static") )

   CStaticLink() :
      m_colorUnvisited( RGB(0,0,255) ),
      m_colorVisited( RGB(128,0,128) ),
      m_bVisited( FALSE ),
      m_hFont( NULL )
   {
   }

   void SetLinkText( LPCTSTR szLink ) {
      USES_CONVERSION;
      m_bstrLink = T2OLE( szLink );
   }

   BEGIN_MSG_MAP(CStaticLink)
      // uses message reflection: WM_* comes back as OCM_*
      MESSAGE_HANDLER( OCM_COMMAND, OnCommand )
      MESSAGE_HANDLER( OCM_CTLCOLORSTATIC, OnCtlColor )
      MESSAGE_HANDLER( WM_DESTROY, OnDestroy ) // not a reflected message
      DEFAULT_REFLECTION_HANDLER()
   END_MSG_MAP()

   LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& ) {
      if( m_hFont ) DeleteObject( m_hFont );
      return 0;
   }

   LRESULT OnCommand( UINT, WPARAM wParam, LPARAM, BOOL& ) {
      USES_CONVERSION;
      int code = HIWORD( wParam );
      if( code == STN_CLICKED || code == STN_DBLCLK ){
         if( m_bstrLink.Length() == 0 ){
            GetWindowText( &m_bstrLink );
         }
         if( (int)ShellExecute( *this, _T("open"),
            OLE2T(m_bstrLink), NULL, NULL, SW_SHOWNORMAL ) > 32 ){
            m_bVisited = TRUE;   // return codes > 32 => success
            Invalidate();
         }else{
            MessageBeep( 0 );
            ATLTRACE( _T("Error: CStaticLink couldn''t open file") );
         }
      }
      return 0;
   }

   LRESULT OnCtlColor( UINT, WPARAM wParam, LPARAM, BOOL& ) {
      // notify bit must be set to get STN_* notifications
      ModifyStyle( 0, SS_NOTIFY );
      HBRUSH hBr = NULL;
      if( (GetStyle() & 0xff) <= SS_RIGHT ){
         // it''s a text control: set up font and colors
         if( !m_hFont ){
            LOGFONT lf;
            GetObject( GetFont(), sizeof(lf), &lf );
            lf.lfUnderline = TRUE;
            m_hFont = CreateFontIndirect( &lf );
         }
         HDC hDC = (HDC)wParam;
         SelectObject( hDC, m_hFont );
         SetTextColor( hDC, m_bVisited ? m_colorVisited
                                       : m_colorUnvisited );
         SetBkMode( hDC, TRANSPARENT );
         hBr = (HBRUSH)GetStockObject( HOLLOW_BRUSH );
      }
      return (LRESULT)hBr;
   }

private:
   COLORREF m_colorUnvisited;
   COLORREF m_colorVisited;
   BOOL m_bVisited;
   HFONT m_hFont;
   CComBSTR m_bstrLink;
}; // CStaticLink

class CReflectDlg : public CDialogImpl<CReflectDlg> {
public:
   enum { IDD = IDD_DIALOG1 };
   
   BEGIN_MSG_MAP(CReflectDlg)
      COMMAND_RANGE_HANDLER( IDOK, IDCANCEL, OnClose )
      MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
      REFLECT_NOTIFICATIONS()      // reflect messages back to static links
   END_MSG_MAP()
      
   LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
   {
      CenterWindow( GetParent() );
      // a textual static control:
      s1.SubclassWindow( GetDlgItem(IDS_TEST1) );
      // a static control displaying an icon
      s2.SubclassWindow( GetDlgItem(IDS_TEST2) );
      // set the icon''s link
      s2.SetLinkText( _T("http://www.microsoft.com") );
      return 1;
   }
   
   LRESULT OnClose(UINT, WPARAM wID, HWND, BOOL& )
   {
      ::EndDialog( m_hWnd, wID );
      return 0;
   }
private:
   CStaticLink s1, s2;
}; // CReflectDlg

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
   _Module.Init( NULL, hInstance );

   CReflectDlg dlg;
   dlg.DoModal();

   _Module.Term();
   return 0;
}
posted on 2007-03-13 10:07 jay 阅读(824) 评论(0)  编辑 收藏 引用 所属分类: ATL

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理