——-先以CButton为例讲解MFC中动态创建控件———
在对话框类中增加了以下3个成员变量:
CButton m_btn1;
CButton m_btn2;
CButton m_btn3;
在对话框的OnInitDialog函数中写入了如下代码动态创建控件
BOOL OnInitDialog
{
m_btn1.CreateEx(0, TEXT("BUTTON"), TEXT("Btn_1"), WS_CHILD | WS_VISIBLE, 10, 10, 100, 20,
this->GetSafeHwnd(), (HMENU)IDC_BTN_DYN_BEG, 0);
m_btn2.CreateEx(0, TEXT("BUTTON"), NULL, WS_CHILD | WS_VISIBLE, 10, 40, 100, 20,
this->GetSafeHwnd(), (HMENU)IDC_BTN_DYN_BEG+1, 0);
m_btn2.SetWindowText(TEXT("Btn_2"));
m_btn3.Create(TEXT("Btn_3"), WS_CHILD | WS_VISIBLE, CRect(10, 70, 110, 90), this, IDC_BTN_DYN_BEG+4);
}
我们现在来研究一下代码的本质:
m_btn1.CreateEx与m_btn2.CreateEx实际上调用的CWnd::CreateEx
m_btn3.Create调用了CButton::Create
先来瞧瞧CButton::Create,其中调用了CWnd::Create
BOOL CButton::Create(LPCTSTR lpszCaption, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID)
{
CWnd* pWnd = this;
return pWnd->Create(_T("BUTTON"), lpszCaption, dwStyle, rect, pParentWnd, nID);
}
CWnd::Create调用了CWnd::CreateEx
BOOL CWnd::Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext)
{
return CreateEx(0, lpszClassName, lpszWindowName,
dwStyle | WS_CHILD,
rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), (HMENU)(UINT_PTR)nID, (LPVOID)pContext);
}
研究CWnd::CreateEx才能看出本质
[wincore.cpp]
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this); //利用Hook进行了子类化
HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
return TRUE;
}
[wincore.cpp]
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
pThreadState->m_pWndInit == pWnd;
if (pThreadState->m_hHookOldCbtFilter == NULL)
{
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
}
pThreadState->m_pWndInit = pWnd;
}
[wincore.cpp]
LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
pWndInit->Attach(hWnd);
pWndInit->PreSubclassWindow();
WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
WNDPROC afxWndProc = AfxGetAfxWndProc();
//改写了窗口过程函数为MFC通用的窗口过程函数也即子类化
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
(DWORD_PTR)afxWndProc);
if (oldWndProc != afxWndProc)
*pOldWndProc = oldWndProc;
pThreadState->m_pWndInit = NULL;
}
所以动态创建控件全部都进行了子类化
以下列出了MFC中具体控件对应的窗口类名称
(由于BUTTON按钮对应了PUSHBUTTON&CHECKBOX&RADIOBOX等,按钮样式区分了创建按钮的类型)
显然:
Button———- CButton—— “BUTTON” — BS_PUSHBUTTON
Check Box——- CButton—— “BUTTON” — BS_AUTOCHECKBOX
Radio Button—- CButton—— “BUTTON” — BS_AUTORADIOBUTTON
Edit Control—- CEdit——– “EDIT”
Combo Box——- CComboBox—- “COMBOBOX”
List Box——– CListBox—– “SysListView32”
如果要动态创建Check Box,代码如下
CButton m_btnChk;
m_btnChk.CreateEx(0, TEXT("BUTTON"), NULL, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 10, 160, 100, 20,
this->GetSafeHwnd(), (HMENU)1250, 0);
在CButton实例对象调用CreateEx时指定窗口类名为”EDIT”都可以,只不过创建的控件就是Edit Box了
m_btn1.CreateEx(0, TEXT("EDIT"), TEXT("Btn_1"), WS_CHILD | WS_VISIBLE, 10, 10, 100, 20,this->GetSafeHwnd(), (HMENU)IDC_BTN_DYN_BEG, 0);