Link to home
Start Free TrialLog in
Avatar of mrwad99
mrwad99Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Handling WM_NCPAINT message in a none CWnd derived class!

Ah hello.

I need to specially handle painting messages, in particular WM_NCPAINT, in my frame windows/dialogs.  For this reason, I am trying to use a special class that handles just these messages:

// .h
class CPaintHandler : public CCmdTarget
{
      DECLARE_DYNAMIC(CPaintHandler)

public:
      CPaintHandler( CWnd* pwndTarget);
      virtual ~CPaintHandler();
      virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo);
protected:
      DECLARE_MESSAGE_MAP()
      CWnd* m_pwndTarget;
};

// .cpp
BOOL CPaintHandler::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
      switch ( nID )
      {
      case WM_NCPAINT:
            {
                  ASSERT ( FALSE );
                  return 1;
            }
      }

      return FALSE;
}

// Dialog app calling code:
// Assume m_pPaintHandler is a CPaintHandler, constructed with "this" via DialogAppDlg::OnInitDialog().

BOOL DialogAppDlg::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
      BOOL bRet = m_pPaintHandler ? m_pPaintHandler->OnCmdMsg ( nID, nCode, pExtra, pHandlerInfo ) : FALSE;
      if ( !bRet )
            bRet = CDialog::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

      return bRet;

}
As you can see, I have added a dummy handler into my CPaintHandler class for the WM_NCPAINT message.  However, this is never getting called.  

Now, I looked into OnCmdMsg, and found that it only deals with the handling of command user-interface objects.  This is hence probably why I am not getting CPaintHandler::OnCmdMsg() called.

I cannot see another way around this however.

Can anyone suggest a fix please?

Thanks in advance.
Avatar of jkr
jkr
Flag of Germany image

That dummy handler is probably never called because you don't seem to have a message map entry for it. BTE, if you want to handle messages in MFC classes, I'd rather derive from CWnd than from CCmdTarget - the latter is the base class for CWnd anyway, see the MFC hierarchy chart.
Avatar of mrwad99

ASKER

Yeah, I tried adding the handler but it would not let me:

BEGIN_MESSAGE_MAP(CPaintHandler, CCmdTarget)
      ON_WM_NCPAINT()
END_MESSAGE_MAP()

/*afx_msg*/ void CPaintHandler::OnNcPaint()
{
}

Gives me:

'static_cast' : cannot convert from 'void (__thiscall CPaintHandler::* )(void)' to 'void (__thiscall CWnd::* )(void)'

>> BTE, if you want to handle messages in MFC classes...

I thought of that, but I want to have one class that is responsible for handling the paint messages in windows *and* dialogs.  If I derive from CWnd:

CPaintHandler : public CWnd {};

CMyWnd : public CPaintHandler {};

CMyDialog : public CDialog, public CMyWnd {};

I cannot do the above as I would need to use virtual inheritance, but I would have to put the virtual keyword at the CWnd level, which would mean modifying the MFC code.  Hence I chose to use CCommandTarget...But it also seems I will have this problem anyway since CWnd is derived from CCommandTarget....arghhhhhhhhh!

Any ideas on how to get around this?

(The end result is that I want to have one class responsible for handling all painting related messages in the window that it is created within.  The message handlers can then call virtual functions, which derived classes can then override as suitable.)

Would a hook, hooking all messages, be suitable do you think?

TIA
Have you tried adding the message handler likke the following?
BEGIN_MESSAGE_MAP(CPaintHandler, CCmdTarget)
      ON_MESSAGE( WM_NCPAINT,OnNcPaint)
END_MESSAGE_MAP()
 
LRESULT CPaintHandler::OnNcPaint(WPARAM, LPARAM)
{
  return 0;
}

Open in new window

Avatar of mrwad99

ASKER

OOO, had a glimmer of hope that that was the answer, but no:

'static_cast' : cannot convert from 'LRESULT (__thiscall CPaintHandler::* )(WPARAM,LPARAM)' to 'LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)'

?
OK, let's try to be brutal:
BEGIN_MESSAGE_MAP(CPaintHandler, CCmdTarget)
{ WM_NCPAINT, 0, 0, 0, AfxSig_lwl, \
  (AFX_PMSG)(AFX_PMSGW) \
  (static_cast< LRESULT (AFX_MSG_CALL CPaintHandler::*)(WPARAM, LPARAM)>\
   (OnNcPaint)) },
END_MESSAGE_MAP()

Open in new window

Or, maybe better:
BEGIN_MESSAGE_MAP(CPaintHandler, CCmdTarget)
{ WM_NCPAINT, 0, 0, 0, AfxSig_lwl,(AFX_PMSG)(AFX_PMSGW)(static_cast< LRESULT (AFX_MSG_CALL CPaintHandler::*)(WPARAM, LPARAM)>(OnNcPaint)) },
END_MESSAGE_MAP()

Open in new window

Avatar of mrwad99

ASKER

Heh, we are getting somewhere: that now compiles at last, so thanks.

Now.  The problem is that the handler is not getting called.  In fact, I added a breakpoint on DialogAppDlg::OnCmdMsg(), with the condition that nID was 0x85 (WM_NCPAINT).  The breakpoint never gets hit, so my CPaintHandler never gets called.  Now, I looked into OnCmdMsg, and found that it only deals with the handling of command user-interface objects.  WM_NCPAINT is not a command user-interface related message, so I guess that is why it is not being called.

Hmm...

I thought about WindowProc, that would do it, but then my CPaintHandler can't have a WindowProc because it is not a window!

Stuck.

Any ideas?

>>The problem is that the handler is not getting called.

I was more afraid that there might be a crash ;o)

Could you try to add a 'TRACE()' to 'OnNcPaint()' and check the debug output? If that does not work also, a local hook really might be the way to go...
Er, forget the last comment - if there is no window associated with the class, you won't receive messages anyway...
Avatar of mrwad99

ASKER

OK.  I might have to look into that then.  

A quick overview of what I would like to achieve:

I want all windows in my app to follow a "theme".  I have the functionality for painting various areas of windows all ready to go, but I don't want to have to keep duplicating the same code over and over for dialogs and frame windows etc.  I want it all in one place.

My first thought was to have "CThemedWnd", derived from CWnd.  I could derive dialogs and frames from that, but wait: the virtual inheritance problem I mentioned above.

(CThemedDlg : public CThemedWnd, public CDialog gives us two CWnds: the virtual keyword would need to go at the CWnd level, which I can't do as it is not my source code).

So I thought "put the functionality for  theming in a helper object.  It can handle the painting messages etc.  Each window to be themed could have one of these objects in each window to be themed, virtual functions could be exploited etc etc: lovely.

But then the problems I encountered as above.

Having (hopefully :) ) read that, is there any other way to do this other than hooking my "window to be themed" for messages, handling those I am interested in that way (if that is possible - is it?)

TIA
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mrwad99

ASKER

Thank you.  If I have any problems with the hooking, expect another question from me :)
Thsnx. No problem ;o)