none
ccustom_control::OnCreate() not called RRS feed

  • Question

  • Through the generous assistance of RLWA32 I was able to add a custom control to a dialog. I now have the problem of the custom control not receiving WM_CREATE messages. As you can see below the message map includes the WM_CREATE message id. Even though "ccustom_control::OnPaint()" and "ccustom_control::OnLButtonDown()" are called "ccustom_control::OnCreate()" is never called. What must I do to ensure that it is called? Thank you kindly. Cheerios 

    BEGIN_MESSAGE_MAP(ccustom_control, CWnd)
    ON_WM_PAINT()
    ON_WM_CREATE()
    ON_WM_LBUTTONDOWN()
    END_MESSAGE_MAP()

    PS am utilizing VS Community 2017

    Tuesday, August 13, 2019 6:08 AM

Answers

  • I followed the "Adding Controls By Hand" instructions at https://docs.microsoft.com/en-us/cpp/mfc/adding-controls-by-hand So I removed the custom control from the dialog template in the resource file. I then created the control manually as instructed from within OnInitDialog(). Now everything works fine ccustom_control::OnCreate() is called. Thank you all for your kind assistance. It would still be nice to know how to arrange for ccustom_control::OnCreate() to be called without having to do things manually which prevents utilizing the dialog editor for positioning and sizing the control which I needed to calculate estimate and readjust so it can be specified in m_custom_control.Create(...). Cheerios
    Tuesday, August 13, 2019 8:03 PM
  • So I removed the custom control from the dialog template in the resource file. I then created the control manually as instructed from within OnInitDialog(). Now everything works fine ccustom_control::OnCreate() is called.

    Which is exactly what I said earlier.

     It would still be nice to know how to arrange for ccustom_control::OnCreate() to be called without having to do things manually which prevents utilizing the dialog editor for positioning and sizing the control which I needed to calculate estimate and readjust so it can be specified in m_custom_control.Create(...).

    Use a placeholder control in your dialog template.  For example, size and position a static control (be sure to give it its own control id) where you want your custom control to be.  Then, in OnInitDialog you can retrieve the size and placement of the placeholder and use that information when you create your custom control.  Finally, delete the placeholder control using DestroyWindow()


    Tuesday, August 13, 2019 8:20 PM

All replies

  • Hello PaltryProgrammer,

    This forum is for "Discuss general issues about developing applications for Windows."

    Since this issue is MFC related I'll move it to MFC forum for more professional support.

    Best regards,

    Jeffrey


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, August 13, 2019 6:33 AM
  • Through the generous assistance of RLWA32 I was able to add a custom control to a dialog. I now have the problem of the custom control not receiving WM_CREATE messages. As you can see below the message map includes the WM_CREATE message id. Even though "ccustom_control::OnPaint()" and "ccustom_control::OnLButtonDown()" are called "ccustom_control::OnCreate()" is never called. What must I do to ensure that it is called? Thank you kindly. Cheerios 

    BEGIN_MESSAGE_MAP(ccustom_control, CWnd)
    ON_WM_PAINT()
    ON_WM_CREATE()
    ON_WM_LBUTTONDOWN()
    END_MESSAGE_MAP()

    PS am utilizing VS Community 2017

    Hello,

    how do you create and open the dialog?

    Regards, Guido

    Edit: And have you checked the return values of the creation and opening of the dialog?
    Tuesday, August 13, 2019 6:39 AM
  • The dialog is a dialog application and is not a dialog as part of or invoked by another window. An excerpt of the invoking code as generated by the IDE is below:

    BOOL CMFCDlgApp::InitInstance()
    {
    // InitCommonControlsEx() is required on Windows XP if an application
    // manifest specifies use of ComCtl32.dll version 6 or later to enable
    // visual styles.  Otherwise, any window creation will fail.
    INITCOMMONCONTROLSEX InitCtrls;
    InitCtrls.dwSize = sizeof(InitCtrls);
    // Set this to include all the common control classes you want to use
    // in your application.
    InitCtrls.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&InitCtrls);

    CWinApp::InitInstance();

    AfxEnableControlContainer();

    // Create the shell manager, in case the dialog contains
    // any shell tree view or shell list view controls.
    CShellManager *pShellManager = new CShellManager;

    // Activate "Windows Native" visual manager for enabling themes in MFC controls
    CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));

    // Standard initialization
    // If you are not using these features and wish to reduce the size
    // of your final executable, you should remove from the following
    // the specific initialization routines you do not need
    // Change the registry key under which our settings are stored
    // TODO: You should modify this string to be something appropriate
    // such as the name of your company or organization
    SetRegistryKey(_T("Local AppWizard-Generated Applications"));

    CMFCDlgDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
    ...
    Tuesday, August 13, 2019 6:54 AM
  • Sorry, I misunderstood. Ok: you have a modal dialog which has a custom control inside, but the OnCreate of the custom control is not called when you create the control.

    Where and how do you create the custom_control in the dialog?

    Regards, Guido

    Tuesday, August 13, 2019 7:13 AM
  • Sorry, I misunderstood. Ok: you have a modal dialog which has a custom control inside, but the OnCreate of the custom control is not called when you create the control.

    Where and how do you create the custom_control in the dialog?

    Regards, Guido

    In Addition:

    Do you have a Create function of your custom control?

    BOOL Ccustom_control::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
    {
        ASSERT(pParentWnd->GetSafeHwnd());
    
        if (!CWnd::Create(CUSTOMCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
            return FALSE;
    
        //now you can do what you want to do in OnCreate
        ...
    }
    

    IMO, you don't need OnCreate, put your code in the Create-function.

    Regards, Guido

    Tuesday, August 13, 2019 7:29 AM
  • I do not know where or how the custom control is created. As far as I know it is MFC magic. If it helps all I can say is that a ccustom_control data member is part of the dialog class. See below (at bottom of class). Thank you for your "Create()" suggestion however when I attempted it I found that according to the debugger "ccustom_control::Create(...)" is not called. Cheerios

    // CMFCDlgDlg dialog
    class CMFCDlgDlg : public CDialogEx
    {
    // Construction
    public:
    CMFCDlgDlg(CWnd* pParent = nullptr); // standard constructor

    // Dialog Data
    #ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MFCDLG_DIALOG };
    #endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

    // Implementation
    protected:
    HICON m_hIcon;

    // Generated message map functions
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
    public:
    ccustom_control m_custom_control_1;
    };
    Tuesday, August 13, 2019 7:45 AM
  • I do not know where or how the custom control is created. As far as I know it is MFC magic. If it helps all I can say is that a ccustom_control data member is part of the dialog class. See below (at bottom of class). Thank you for your "Create()" suggestion however when I attempted it I found that according to the debugger "ccustom_control::Create(...)" is not called. Cheerios

    // CMFCDlgDlg dialog
    class CMFCDlgDlg : public CDialogEx
    {
    // Construction
    public:
    CMFCDlgDlg(CWnd* pParent = nullptr); // standard constructor

    // Dialog Data
    #ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MFCDLG_DIALOG };
    #endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

    // Implementation
    protected:
    HICON m_hIcon;

    // Generated message map functions
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
    public:
    ccustom_control m_custom_control_1;
    };

    Ok, you have inserted the control as a normal member. I hope you have set the variable through resource editor. Then you don't need to call Create of the control explicitly. The environment will do it for you.

    Something is missing in your control class (e.g. IMPLEMENT_DYNCREATE(Ccustomcontrol, CWnd)? ).
    Show us the code of your control class.

    Regards, Guido


    Tuesday, August 13, 2019 7:55 AM
  • The windows for controls contained in a dialog template are created  at a very early point in time when the dialog itself is first created.  Consequently, the control has received its WM_CREATE message before the member variable for the custom control (and its handler functions) have been connected to the control.

    If you had manually created the custom control through code by calling its Create function then OnCreate would have been called.


    • Edited by RLWA32 Tuesday, August 13, 2019 10:09 AM clarified explanation
    Tuesday, August 13, 2019 10:02 AM
  • Thank you for your IMPLEMENT_DYNCREATE suggestion but upon attempting it I found that according to the debugger neither "ccustom_control::Create(...) nor "ccustom_control::OnCreate()" are called. the ccustom_control files are below.

    ccustom_control.h

    #pragma once
    #include <afxwin.h>
    
    #define CUSTOMCONTROLWNDCLASSNAME L"custom_control"
    
    class ccustom_control :
    	public CWnd
    {
    	DECLARE_DYNCREATE(ccustom_control)
    public:
    	ccustom_control();
    	virtual ~ccustom_control();
    	DECLARE_MESSAGE_MAP()
    	afx_msg void OnPaint();
    private:
    	static void register_class();
    public:
    	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    	BOOL Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle);
    };
    

    ccustom_control.cpp

    #include "stdafx.h"
    #include <string>
    #include "ccustom_control.h"
    
    using namespace std;
    
    ccustom_control::ccustom_control()
    	: CWnd()
    {
    	register_class();
    }
    
    ccustom_control::~ccustom_control()
    {
    }
    
    IMPLEMENT_DYNCREATE(ccustom_control, CWnd)
    
    BEGIN_MESSAGE_MAP(ccustom_control, CWnd)
    	ON_WM_PAINT()
    	ON_WM_CREATE()
    	ON_WM_LBUTTONDOWN()
    END_MESSAGE_MAP()
    
    void ccustom_control::register_class()
    {
    	WNDCLASS windowclass;
    	HINSTANCE hInst = AfxGetInstanceHandle();
    	wstring wclass_name(CUSTOMCONTROLWNDCLASSNAME);
    
    	//Check whether the class is registerd already
    	if (!(::GetClassInfo(hInst, wclass_name.c_str(), &windowclass)))
    	{
    		//If not then we have to register the new class
    		windowclass.style = CS_DBLCLKS;// | CS_HREDRAW | CS_VREDRAW;
    		windowclass.lpfnWndProc = ::DefWindowProc;
    		windowclass.cbClsExtra = windowclass.cbWndExtra = 0;
    		windowclass.hInstance = hInst;
    		windowclass.hIcon = NULL;
    		windowclass.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    		windowclass.hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
    		windowclass.lpszMenuName = NULL;
    		windowclass.lpszClassName = wclass_name.c_str();
    		if (!AfxRegisterClass(&windowclass))
    			AfxThrowResourceException();
    	}
    }
    
    void ccustom_control::OnPaint()
    {
    	CPaintDC dc(this); // device context for painting
    					   // TODO: Add your message handler code here
    					   // Do not call CWnd::OnPaint() for painting messages
    	CBrush brush0(0x00cc00);
    	dc.FillRect(&dc.m_ps.rcPaint, &brush0);
    }
    
    
    int ccustom_control::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	if (CWnd::OnCreate(lpCreateStruct) == -1)
    		return -1;
    
    	// TODO:  Add your specialized creation code here
    
    	return 0;
    }
    
    
    void ccustom_control::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	BOOL ok = MessageBeep(MB_ICONASTERISK);
    	assert(ok);
    	CWnd::OnLButtonDown(nFlags, point);
    }
    
    
    BOOL ccustom_control::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
    {
    	ASSERT(pParentWnd->GetSafeHwnd());
    
    	if (!CWnd::Create(CUSTOMCONTROLWNDCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
    		return FALSE;
    
    	//now you can do what you want to do in OnCreate
    	return(TRUE);
    }

    Tuesday, August 13, 2019 11:05 AM
  • Think about the following sequence of events -

    1) You instantiate your dialog class (CMFCDlgDlg) in the application's InitInstance function.  This causes your custom_control class constructor to be called since CMFCDlgDlg has a custom_control member variable.

    2) The custom_control class constructor registers a window class.  However, the registered class sets its window procedure to DefWindowProc, the default Windows window procedure.

    3) DoModal is called.  This causes the creation of the dialog and all of the controls that it contains.  However, the window procedure for the custom control is DefWindowProc.  It will receive all window creation messages.

    4) Later in the process, DoDataExchange is called to connect the custom_control variable to the existing window.  Only after this happens will the message handlers in the custom control class start getting called.

    Tuesday, August 13, 2019 12:20 PM
  • Dialog boxes expect the following layout.

    1) The window procedure, WNDCLASS(EX)'s lpfnWndProc, to be set to DefDlgProc or a window procedure that calls DefDlgProc and not DefWindowProc.

    2) The WNDCLASS(EX)'s cbWndExtra to be set to DLGWINDOWEXTRA.

    3) You set the dialog procedure using SetWindowLongPtr using DWLP_DLGPROC.

    This exact layout is needed for dialog boxes to work as you expect. DefDlgProc is what implements the dialog boxes basic behaviour. It is programmed to basically use:

    CallWindowProc(GetWindowLongPtr(wnd, DWLP_DLGPROC), wnd, msg, wparam, lparam)

    after it has done any necessary work. This means you need space for the dialog procedure and you need that space to be set to the dialog procedure.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Tuesday, August 13, 2019 1:34 PM
    Tuesday, August 13, 2019 1:34 PM
  • Darran,

    I think you may have misunderstood.  The OP is questioning why an MFC user control's OnCreate override is not called during when MFC's standard DoModal function is called.


    • Edited by RLWA32 Tuesday, August 13, 2019 1:50 PM
    Tuesday, August 13, 2019 1:48 PM
  • Your right, I was focused on the dialog and missed that.

    Then in that case.

    How do you expect the message to get through to the MFC message handler? DefWindowProc doesn't know about any of this and it doesn't call any of the class members.

    Whenever I use a class based window approach, I always have to write a special window procedure that understands that this is part of a class.

    The implementation was along the lines of:

    template<typename T>
    class window_base
    {
    public:
    	using my_t = T;
    	using myptr_t = T *;
    
    	static myptr_t get_this_from_handle(_In_ HWND);
    	static LRESULT CALLBACK wnd_proc(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM); //this is what WNDCLASSEX.lpfnWndProc was set to
    
    protected:
    	HWND get_handle() const;
    
    	LRESULT message_handler(_In_ UINT, _In_ WPARAM, _In_ LPARAM); //this is the fallback message handler
    };

    The window procedure then obtained the class instance from the window and called the message_handler function.

    template<typename T>
    inline LRESULT CALLBACK window_base<T>::wnd_proc(_In_ HWND wnd, _In_ UINT msg, _In_ WPARAM wparam, _In_ LPARAM lparam)
    {
    	switch (msg)
    	{
    	case WM_NCCREATE:
    	{
    		//stuff needed to be done when the window is created
    	}
    	case WM_NCDESTROY:
    	{
    		//stuff needed to be done when the window is being destroyed
    	}
    	default:
    	{
    		myptr_t that = get_this_from_handle(wnd);
    		if (that)
    		{
    			return that->message_handler(msg, wparam, lparam);
    		}
    		return DefWindowProcW(wnd, msg, wparam, lparam);
    	}
    	}
    }

    This is the kind thing that needs to be done. Now, notice that DefWindowProc is called as the fallback? It means that it can't know about any of the class based things. So DefWindowProc will just handle the message in the default way and return.

    As far as I'm aware, the window procedure for MFC controls should be set to AfxWndProc. This is implemented in such a way that it will respond to WM_QUERYAFXWNDPROC and return 1, and it will obtain the class instance from the window information and call through to the class's message map.

    From the Visual Studio 2019's MFC, it is implemented as:

    ////////////////////////////////////////////////////////////////////////////
    // The WndProc for all CWnd's and derived classes
    
    LRESULT CALLBACK
    AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
    	// special message which identifies the window as using AfxWndProc
    	if (nMsg == WM_QUERYAFXWNDPROC)
    		return 1;
    
    	// all other messages route through message map
    	CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
    	ASSERT(pWnd != NULL);
    	ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd);
    	if (pWnd == NULL || pWnd->m_hWnd != hWnd)
    		return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
    	return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
    }

    So the WNDCLASS(EX)'s lpfnWndProc must be set to AfxWndProc.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Tuesday, August 13, 2019 2:38 PM
  • In my copy of VS2015 when I tried setting a custom control class lpfnWndProc to AfxWndProc the application asserted because a null pointer was returned from

    	CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

    Tuesday, August 13, 2019 2:53 PM
  • Hmm, that is interesting. That must mean that the MFC is more horrible than I thought.

    Does this mean that it installs a hook to then fiddle with the window procedure as the window is being created?


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Tuesday, August 13, 2019 3:18 PM
  • I've not looked into the specifics of how MFC creates dialogs and controls.

    But even if you take a standard control in a dialog box like a button or an edit control, derive your own class from CEdit or CButton, and hook them up in DoDataExchange the derived classes OnCreate functions are not called when the controls are in the dialog template.

    Tuesday, August 13, 2019 3:43 PM
  • I did a quick step through a portion of MFC's DoModal().  I didn't bother going any further than when I saw it install a CBT hook during dialog creation in a called function -

    void AFXAPI AfxHookWindowCreate(CWnd* pWnd)

    Tuesday, August 13, 2019 3:58 PM
  • I followed the "Adding Controls By Hand" instructions at https://docs.microsoft.com/en-us/cpp/mfc/adding-controls-by-hand So I removed the custom control from the dialog template in the resource file. I then created the control manually as instructed from within OnInitDialog(). Now everything works fine ccustom_control::OnCreate() is called. Thank you all for your kind assistance. It would still be nice to know how to arrange for ccustom_control::OnCreate() to be called without having to do things manually which prevents utilizing the dialog editor for positioning and sizing the control which I needed to calculate estimate and readjust so it can be specified in m_custom_control.Create(...). Cheerios
    Tuesday, August 13, 2019 8:03 PM
  • So I removed the custom control from the dialog template in the resource file. I then created the control manually as instructed from within OnInitDialog(). Now everything works fine ccustom_control::OnCreate() is called.

    Which is exactly what I said earlier.

     It would still be nice to know how to arrange for ccustom_control::OnCreate() to be called without having to do things manually which prevents utilizing the dialog editor for positioning and sizing the control which I needed to calculate estimate and readjust so it can be specified in m_custom_control.Create(...).

    Use a placeholder control in your dialog template.  For example, size and position a static control (be sure to give it its own control id) where you want your custom control to be.  Then, in OnInitDialog you can retrieve the size and placement of the placeholder and use that information when you create your custom control.  Finally, delete the placeholder control using DestroyWindow()


    Tuesday, August 13, 2019 8:20 PM