2013-07-07 74 views
1

我有一个自定义窗口封装在一个静态WndProc函数的类中,以处理由它生成的消息。现在我有一个子类,它实现了消息处理过程中的一些特性,与父类不同。处理来自子类的Windows消息

例如,在下面的代码中,子类中的WM_LBUTTONDOWN发生的情况与父类中发生的情况有所不同。

我想过多态性,但我认为它不会工作,因为从父类调用:: SetWindowLongPtr(),并且“this”指针传递给属于父类,纠正我,如果我错了。

如果我错了,多态性将在这种情况下工作,那么也有一些消息,这些消息不是由父类处理,应该在子类中处理,并将一个空虚拟函数放在父类中,仅用于这似乎并不干净,除了很难为窗口产生的每一条消息提供一个空的虚拟函数外,这些消息仅仅是为了将来它将被使用。

会有几个这样的子类,每个子类对于某些消息的行为不同,但不是全部。

那么,我该怎么去做。

parent.cpp

parent::parent() 
{ 

    WNDCLASSEX wincl; 

    wincl.hInstance   = hInstance; 
    wincl.lpszClassName  = "parent"; 
    wincl.lpfnWndProc  = WndProc; 
    wincl.style    = CS_BYTEALIGNWINDOW; 
    wincl.cbSize   = sizeof (WNDCLASSEX); 
    wincl.hIcon    = 0; 
    wincl.hIconSm   = 0; 
    wincl.hCursor   = ::LoadCursor (NULL, IDC_ARROW); 
    wincl.lpszMenuName  = NULL; 
    wincl.cbClsExtra  = 0; 
    wincl.cbWndExtra  = 4; 
    wincl.hbrBackground  = ::CreateSolidBrush(backgroundColor); 

    ::RegisterClassEx (&wincl); 

    hwnd = ::CreateWindowEx (0, "parent", txt.c_str(), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILD, x, y, width, height, parent, 0, hInstance, 0) ; 

    ::SetWindowLongPtr(hwnd , GWLP_USERDATA , (LONG) this) ; 

} 



LRESULT CALLBACK parent::WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 


    view::parent* panel = reinterpret_cast < view::parent* > ( ::GetWindowLongPtr (hwnd , GWLP_USERDATA) ); 


    switch (message) 
    {  


     case WM_CREATE: 

      ::SendMessage (hwnd, WM_SETFONT, (WPARAM) panel->hFont, (LPARAM) true); 

      break ; 



     case WM_COMMAND: 


      return panel->command (message, wParam, lParam); 

      break ; 



     case WM_LBUTTONDOWN: 


      return panel->lButton (message, wParam, lParam); 

      break; 



     case WM_RBUTTONDOWN: 


      return panel->rButton (message, wParam, lParam); 

      break; 



     case WM_ERASEBKGND: 


      return 1; 

      break; 



     case WM_PAINT: 


      return panel->paint (); 


      break ; 



     default: 


     return ::DefWindowProc (hwnd, message, wParam, lParam); 


    } 

    return 0 ; 


}; 

感谢。

+0

一种常见的方法来做到这是创建虚拟或纯虚成员函数,例如'虚拟LRESULT onButtonDown(...){}'等 –

+0

@JoshGreifer所以你说在这种情况下Polymorphism将工作,即使“this”指针传递给:: SetWindowLongPtr()属于父类? – StudentX

+3

使'parent :: WndProc'虚拟。派生类重写'child :: WndProc',处理它们想要重写的消息,对于那些他们不想重写的消息,将调用转发给'parent :: WndProc'。 [这是一项标准技术](http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.aspx)。 –

回答

1

尝试这样:

class parent 
{ 
private: 
    // ... 
    static LRESULT CALLBACK WndProcCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 
protected: 
    HWND m_hwnd; 
    // ... 
    virtual LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam); 
    virtual LRESULT DefWndProc(UINT message, WPARAM wParam, LPARAM lParam); 
    virtual LRESULT command(WPARAM wParam, LPARAM lParam); 
    virtual LRESULT lButtonDown(WPARAM wParam, LPARAM lParam); 
    virtual LRESULT rButtonDown(WPARAM wParam, LPARAM lParam); 
    virtual LRESULT paint(); 
    // ... 
public: 
    parent(); 
    virtual ~parent(); 
    // ... 
}; 

parent::parent() 
{ 
    WNDCLASSEX wincl = {0}; 

    wincl.hInstance   = hInstance; 
    wincl.lpszClassName  = "parent"; 
    wincl.lpfnWndProc  = WndProcCallback; 
    wincl.style    = CS_BYTEALIGNWINDOW; 
    wincl.cbSize   = sizeof(WNDCLASSEX); 
    wincl.hIcon    = 0; 
    wincl.hIconSm   = 0; 
    wincl.hCursor   = ::LoadCursor(NULL, IDC_ARROW); 
    wincl.lpszMenuName  = NULL; 
    wincl.cbClsExtra  = 0; 
    wincl.cbWndExtra  = 4; 
    wincl.hbrBackground  = ::CreateSolidBrush(backgroundColor); 

    ::RegisterClassEx(&wincl); 

    m_hwnd = NULL; 
    ::CreateWindowEx(0, "parent", txt.c_str(), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILD, x, y, width, height, parent, 0, hInstance, this); 
} 

parent::~parent() 
{ 
    if (m_hwnd) 
     DestroyWindow(m_hwnd); 
} 

LRESULT CALLBACK parent::WndProcCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    parent* panel; 

    if (message == WM_CREATE) 
    { 
     CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT*>(lParam); 
     panel = static_cast<parent*>(cs->lpCreateParams); 
     panel->m_hwnd = hwnd; 
     ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(panel)); 
    } 
    else 
     panel = reinterpret_cast<parent*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); 

    if (panel) 
     return panel->WndProc(message, wParam, lParam); 

    return ::DefWindowProc(hwnd, message, wParam, lParam); 
} 

LRESULT parent::WndProc(UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch (message) 
    {  
     case WM_CREATE: 
      ::SendMessage(m_hwnd, WM_SETFONT, (WPARAM) hFont, TRUE); 
      break ; 

     case WM_COMMAND: 
      return command(wParam, lParam); 
      break ; 

     case WM_LBUTTONDOWN: 
      return lButtonDown(wParam, lParam); 
      break; 

     case WM_RBUTTONDOWN: 
      return rButtonDown(wParam, lParam); 
      break; 

     case WM_ERASEBKGND: 
      return 1; 
      break; 

     case WM_PAINT: 
      return paint(); 
      break; 
    } 

    return DefWndProc(message, wParam, lParam); 
} 

LRESULT parent::DefWndProc(UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    return ::DefWindowProc(m_hwnd, message, wParam, lParam); 
} 

LRESULT parent::command(WPARAM wParam, LPARAM lParam) 
{ 
    return DefWndProc(WM_COMMAND, wParam, lParam); 
} 

LRESULT parent::lButtonDown(WPARAM wParam, LPARAM lParam) 
{ 
    return DefWndProc(WM_LBUTTONDOWN, wParam, lParam); 
} 

LRESULT parent::rButtonDown(WPARAM wParam, LPARAM lParam) 
{ 
    return DefWndProc(WM_RBUTTONDOWN, wParam, lParam); 
} 

LRESULT parent::paint() 
{ 
    return 0; 
} 

这不仅怀抱多态性和封装性,但使WndProc()本身虚拟允许派生类重写任何收到的消息的行为(当然,在WM_CREATE之后收到的任何消息),特别是消息parent没有概念:

class child : public parent 
{ 
protected: 
    LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam); 
}; 

LRESULT child::WndProc(UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    if (message == WM_SOMETHING) 
    { 
     // ... 
     return 0; 
    } 

    return parent::WndProc(message, wParam, lParam); 
} 
+0

非常感谢,我已经想通了,甚至改变了“this”指针传递给回调的方式,完全像你做了一个小修改:P。我有一个问题,如果我正在使用CREATESTRUCT检索指向WM_CREATE中的对象的指针,那么它不起作用,所以我将WM_CREATE更改为WM_NCCREATE,如http://blogs.msdn.com/ b/oldnewthing/archive/2005/04/22/410773.aspx和它的工作,甚至MSDN使用WM_CREATE,那么为什么它不适合我? http://msdn.microsoft.com/en-us/library/ff381400%28VS.85%29.aspx – StudentX

+0

完全定义“不起作用”。 'WM_CREATE'和'WM_NCCREATE'都可以正常工作,'parent *'指针在两条消息的'CREATESTRUCT'结构中都是有效的。然而,我必须做的一个改变是移动'parent :: m_hwnd'成员的赋值,使得它在'parent :: WndProc()'中可用,而'CreateWindowEx()'仍在运行。我已经相应地更新了我的答案。 –