Jason Mars
Jason Mars

Reputation: 11

How to create custom window to be child?

I try to create custom child window that derive from CWnd inside CFrameWnd parent. The child window just rectangle with border style. Child create after parent create. it handle on OnCreate event. But the child window not appears. What's wrong here?

#include <afxwin.h>

class Index: public CWnd
{
  public:
    Index()
    {
      CWnd* parentWnd = AfxGetApp()->m_pMainWnd;

      Create(
        NULL, 
        NULL,
        WS_CHILD | WS_BORDER, 
        CRect(CPoint(0, 0), CSize(100, 100)),
        parentWnd, NULL, NULL);
    };
};

class MainFrame: public CFrameWnd
{
  public:
    MainFrame()
    {
      Create(
        NULL,
        "Parent and Child",
        WS_OVERLAPPEDWINDOW,
        CRect(CPoint(0, 0), CSize(640, 360))
      );
    };

  protected:
    afx_msg int OnCreate(LPCREATESTRUCT);
    DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MainFrame, CFrameWnd)
  ON_WM_CREATE()
END_MESSAGE_MAP()

int MainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  Index* index = new Index;
  index->ShowWindow(SW_NORMAL);
  
  return 0;
};

class Application: public CWinApp
{
  BOOL InitInstance()
  {
    m_pMainWnd = new MainFrame;
    m_pMainWnd->ShowWindow(SW_NORMAL);
    m_pMainWnd->UpdateWindow();

    return true;
  };    
};

Application app;

Upvotes: 0

Views: 739

Answers (2)

JohnCz
JohnCz

Reputation: 1609

Well your post shows only WS_CHILD | WS_BORDER for Index class.

I am back. I was extremely busy, but now I have some time to dive deeper into your code.

I have seen similar code in many places and to be honest this should never see the day. Several things: I never do any allocation in the class’ constructor. Neither memory nor handled. If allocation fails there is no way to gracefully exit the app but crash. There are more appropriate places to call for windows creation.

Your posted code is going to crash since you call Create member of the CWnd class, not CFrameWnd class. It implements Create differently, calling CreateEx. CWnd would ASSERT. That is why I have changed it.

The code posted is not the code you are building with and that makes any attempt to help you much harder. Continuing: you are allocating MainFrame and Index objects on the heap. This will create a memory leak since you do not clean allocations on exit. This does not apply to MainFrame. This class calls delete on PostNcDestroy. This is by design. All Document, View, and Frame classes are designed to be allocated dynamically and release object allocation. You can declare the Index class object as a member variable of the MainFrame. This way it will be allocated on the frame (stack) and deallocated automatically when MainFrame object goes out of scope.

When you call Create to attach Index class to the windows handle, pass AFX_IDW_PANE_FIRST value as the ID. Frame Window will resize the window with this ID automatically. Another piece of advice: keep the definitions and declaration in separate files – .cpp and .h. Also, keep classes in different files. It makes code maintenance much easier. in your case, you need a .cpp file to build exe.

I know this is an exercise, but if you are going to learn it is better to do it correctly from the beginning. I did not separate this snippet into different units to make it easier for you to compare code and see changes.

#include <afxwin.h>

class Index : public CWnd
{
public:
    Index()
    {
    };

    void OnPaint()
    {
        CPaintDC dc(this); // device context for painting  added to indicate that this exists
        CRect recWnd;
        GetClientRect(recWnd);
        dc.DrawText(CString(_T("This is a child window")), CRect(0, 10, recWnd.Width(), 50), DT_CENTER);
    }

    DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP(Index, CWnd)
    ON_WM_PAINT()
END_MESSAGE_MAP()


class MainFrame : public CFrameWnd
{
public:
    MainFrame()
    {
    };

    int OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        // below, keep only one line uncommented

        // if this line is uncommented Index windows will automatically resize. 
        //m_Index.Create(NULL, NULL, WS_CHILD, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL);

        //This version will not 
        m_Index.Create(NULL, NULL, WS_CHILD | WS_BORDER, CRect(0, 0, 300, 100), this, NULL, NULL);
        
        m_Index.ModifyStyleEx(0, WS_EX_CLIENTEDGE); //to better see the client
        m_Index.ShowWindow(SW_NORMAL);

        return 0;
    };

protected:
    Index m_Index;
protected:
    DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MainFrame, CFrameWnd)
        ON_WM_CREATE()
END_MESSAGE_MAP()


class Application : public CWinApp
{
    virtual BOOL InitInstance()
    {
        MainFrame* pMainFrame = new MainFrame;

        pMainFrame->Create(NULL, _T("Parent and Child"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 640, 360));
        m_pMainWnd = pMainFrame;

        m_pMainWnd->ShowWindow(SW_NORMAL);
        m_pMainWnd->UpdateWindow();

        return true;
    };
};

Application app; 

Upvotes: 0

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

When overriding methods such as CWnd::OnCreate or CWinApp::InitInstance, be sure to call the base class.

Avoid creating new windows in the constructor, it makes it very difficult to debug. Create the child windows in parent window's OnCreate method, pass the this to indicate parent:

class Index : public CWnd
{
public:
    Index() {}
};

class MainFrame : public CFrameWnd
{
public:
    MainFrame(){};
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        CFrameWnd::OnCreate(lpCreateStruct);
        Index* index = new Index;
        index->Create(NULL, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
            CRect(0, 0, 400, 300), this, NULL, NULL);
        index->ShowWindow(SW_NORMAL);
        return 0;
    };
    DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MainFrame, CFrameWnd)
    ON_WM_CREATE()
END_MESSAGE_MAP()

class Application : public CWinApp
{
    BOOL InitInstance()
    {
        CWinApp::InitInstance();
        m_pMainWnd = new MainFrame;
        m_pMainWnd->CreateEx(0, AfxRegisterWndClass(0), NULL,
            WS_VISIBLE | WS_OVERLAPPEDWINDOW, 20, 20, 600, 400, NULL, 0);
        return true;
    };
}; Application app;

Upvotes: 1

Related Questions