MFC: OnNcCreate() not called; need to set BS_OWNERDRAW flag for CButton subclass

189 views Asked by At

I'm not an expert on MFC, but I've made a dozen or so custom controls over the last 15+ years. I've just made a CButton with custom graphics.

Here's the problem: Of course I need BS_OWNERDRAW to be set. I have an OnNcCreate() method I've cut and pasted for 15+ years that's always worked in my custom controls, but for some reason it's not now being called.

I have no interest or even understanding of OnNcCreate() except for the fact that it was the place I ended up getting BS_OWNERDRAW set the first time I tried writing a custom control.

Question: What would keep OnNcCreate() from being called? Or, is there another place I could or should be setting BS_OWNERDRAW ?

A working class is defined with:

class CGood : public CStatic {

The non-working one is defined with:

class CBad : public CButton {

They both prototype the method thusly:

afx_msg int     OnNcCreate( LPCREATESTRUCT lpCreateStruct );

The method is written identically (except the class name):

int CGood::OnNcCreate( LPCREATESTRUCT pcrs ) {

  wStyleBits = LOWORD( pcrs->style );

  DWORD dwStyleSuperclass = MAKELONG( ES_LEFT, HIWORD( pcrs->style ) );

  dwStyleSuperclass |= BS_OWNERDRAW;

  ::SetWindowLong( m_hWnd, GWL_STYLE, dwStyleSuperclass );

  pcrs->style = dwStyleSuperclass;

  return CStatic::OnNcCreate( pcrs );
}

The dialog is using both the good and bad classes utilizing the Visual Studio 2017 dialog editor, as CustomControl. Both the good and bad classes have Extended Style 0x0 and Style 0x50010000. Both er Disabled=False, Help ID=False, Visible=True, Class=Good or Bad as needed.

When run the application calls each class's RegisterControlClass() method:

static WNDPROC pfnWndProc = NULL;

BOOL CBad::RegisterControlClass() {

  WNDCLASS wcls;
  static const TCHAR szClass[] = _T( "CBad" );

  if ( ::GetClassInfo( AfxGetInstanceHandle(), szClass, &wcls ) )
      return wcls.lpfnWndProc == ( WNDPROC ) CBad::WndProcHook;

  VERIFY( ::GetClassInfo( NULL, _T( "button" ), &wcls ) );
  pfnWndProc = wcls.lpfnWndProc;

  wcls.lpfnWndProc   = CBad::WndProcHook;
  wcls.hInstance     = AfxGetInstanceHandle();
  wcls.lpszClassName = szClass;

  return RegisterClass( &wcls ) != 0;
}



LRESULT CALLBACK EXPORT CBad::WndProcHook( HWND hWnd, UINT msg,
                                              WPARAM wParam, LPARAM lParam ) {

  CBad* pthis = new CBad();
  pthis->Attach( hWnd );

  pthis->m_pfnSuper = pfnWndProc;
  ::SetWindowLong( hWnd, GWL_WNDPROC, ( DWORD )AfxWndProc );

#ifdef STRICT
  return ::CallWindowProc( AfxWndProc, hWnd, msg, wParam, lParam );
#else
  return ::CallWindowProc( ( FARPROC )AfxWndProc, hWnd, msg, wParam, lParam );
#endif
}

(Funny: I note in CGood, derived from CStatic, mistakenly has "button" there and works fine; it stops working if I change to "static" . I'll look into that later. The bad class has "button" and indeed is a CButton subclass.)

Things I've done to confirm the issue: When I remove the BS_OWNERDRAW from the CGood::OnNcCreate(), it stops calling DrawItem(). So I'm sure that's the place this flag is being set.

Putting a breakpoint in CGood::OnNcCreate() and CBad::OnNcCreate(), the CBad one simply isn't called.

I've found examples on the internet of setting BS_OWNERDRAW in PreSubclassWindow(), OnCreate(), and CreateEx(), none of which work, in some cases for reasons I think I understand. (EG you can't just set flags in OnCreate() and call the superclass method, because it is ultimately getting the flags not from the param you give it but from some other source.)

I've tried changing CBad's superclass to CStatic and still the OnNcCreate() isn't being called.

I've tried setting BS_OWNERDRAW in the dialog's OnInitDialog() and that does get the CBad button subclass painting correctly. So I know I'm not accidently creating them with some other subclass, etc.

CBad* pcbut = (CBad*) GetDlgItem( IDC_MARKCHECK );
pcbut->ModifyStyle(0, BS_OWNERDRAW);
1

There are 1 answers

0
Swiss Frank On

Ahh! I simply forgot to add it to the message map!

BEGIN_MESSAGE_MAP( CBad, CButton )
    //{{AFX_MSG_MAP( CBad )
    ON_WM_NCCREATE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()