Reputation: 11
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
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
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