采用ATL实现无模板对话框的显示
在采用ATL、WTL的开发对话框程序的标准方法是从CDialogImpl上继承,然后在类中定义一个IDD,把这个窗口与资源关联起来。如果不定这个IDD是编译不通过的,代码如下:
class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
enum { IDD = IDD_ABOUTBOX };
BEGIN_MSG_MAP(CAboutDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(IDOK, OnCloseCmd)
COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd)
END_MSG_MAP()
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
};
在有些情况下,我们并不会采用窗口模板来创建窗口,而是对话框及所有的控件都采用自定义的方式来实现,比我们通过根据配置文件来生成控件列表。为了实现这个需求我开发一个无模板对话框类,代码如下:
typedef CWinTraits<WS_POPUP|WS_BORDER|WS_SYSMENU|DS_MODALFRAME|WS_CAPTION,
WS_EX_DLGMODALFRAME | WS_EX_CONTROLPARENT> CDialogWinTraits;
template <class T, class TWinTraits = CDialogWinTraits, class TBase = CWindow>
class ATL_NO_VTABLE CNTDialogImpl : public CDialogImplBaseT< TBase >
{
public:
#ifdef _DEBUG
bool m_bModal;
CNTDialogImpl() : m_bModal(false) { }
#endif //_DEBUG
// modal dialogs
virtual INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), _U_RECT rect = NULL,
LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0)
{
// 创建模板
LPDLGTEMPLATE pTemplate = CreateDlgTemplate (dwStyle, dwExStyle, szWindowName);
if (pTemplate == NULL) return -1;
// 更新位置
if (rect.m_lpRect)
{
pTemplate->x = (short)rect.m_lpRect->left;
pTemplate->y = (short)rect.m_lpRect->right;
pTemplate->cx = (short)(rect.m_lpRect->right - rect.m_lpRect->left);
pTemplate->cy = (short)(rect.m_lpRect->bottom - rect.m_lpRect->top);
}
// 开始显示窗口
ATLASSUME(m_hWnd == NULL);
if (m_thunk.Init(NULL,NULL) == FALSE)
{
SetLastError(ERROR_OUTOFMEMORY);
return -1;
}
_AtlWinModule.AddCreateWndData(&m_thunk.cd, this);
#ifdef _DEBUG
m_bModal = true;
#endif //_DEBUG
LRESULT ret = DialogBoxIndirect(_AtlBaseModule.GetResourceInstance(),
pTemplate, hWndParent, T::StartDialogProc);
GlobalFree(pTemplate);
return ret;
}
BOOL EndDialog(int nRetCode)
{
ATLASSERT(::IsWindow(m_hWnd));
#ifdef _DEBUG
ATLASSUME(m_bModal); // must be a modal dialog
#endif //_DEBUG
return ::EndDialog(m_hWnd, nRetCode);
}
protected:
LPWORD lpwAlign ( LPWORD lpIn)
{
ULONG ul = (ULONG) lpIn;
ul +=3; ul >>= 2; ul <<= 2;
return (LPWORD) ul;
}
// 创建对话框模板
LPDLGTEMPLATE CreateDlgTemplate (DWORD dwStyle, DWORD dwExStyle, LPCTSTR szWindowName)
{
HGLOBAL hgbl = NULL;
LPDLGTEMPLATE lpdt = NULL;
LPWORD lpw = NULL;
LPWSTR lpwsz = NULL;
hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
if (!hgbl) return NULL;
lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
// 定义窗口样式
lpdt->style = TWinTraits::GetWndStyle (dwStyle);
lpdt->dwExtendedStyle = TWinTraits::GetWndExStyle (dwExStyle);
lpdt->cdit = 0; // 窗口中的控件数量
lpdt->x = 0;
lpdt->y = 0;
lpdt->cx = 100; // 默认宽度
lpdt->cy = 50; // 默认高度
lpw = (LPWORD) (lpdt + 1);
*lpw++ = 0x0000; // 没有菜单
*lpw++ = 0x0000; // 采用默认类
lpwsz = (LPWSTR) lpw;
int nchar = 0;
if (szWindowName)
{
int slen = _tcslen(szWindowName) + 1;
_tcsncpy_s (lpwsz, slen, szWindowName, slen);
nchar = slen;
}
lpw += nchar;
GlobalUnlock(hgbl);
return (LPDLGTEMPLATE) hgbl;
}
};
使用方法为把从CDialogImpl上继承改为从CNTDialogImpl中继承就可以,而且不用再定义IDD:
class CAboutDlg : public CNTDialogImpl<CAboutDlg>
{
public:
BEGIN_MSG_MAP(CAboutDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd)
END_MSG_MAP()
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
};
在OnInitDialog中你可以创建你想到的控件,同时最好加上窗口居中的代码,否则窗口会显示在主窗口的左则,不太好看。代码如下:
LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CenterWindow(GetParent()); // 窗口居中
ResizeClient (300, 200); // 调整窗口大小
//在以下窗口控件
//.......
return TRUE;
}