对话框中控件的UI Updating
对话框中的的UI updating控制比MFC中简单得多,在MFC中,你需要响应未公开的WM_KICKIDLE消息,处理这个消息并触发控件的updating,在WTL中,没有这个诡计,不过向导存在一个BUG,需要手工添加一行代码解决这个问题。
首先需要记住的是对话框必须是无模式的,因为CUpdateUI需要在程序的消息循环控制下工作。如果对话框是模式的,系统处理消息循环,我们程序的空闲处理函数就不会被调用,由于CUpdateUI是在空闲时间工作的,所以没有空闲处理就没有UI updating。
ControlMania2的对话框是非模式的,类定义的开始部分很像是一个框架窗口类:
class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>, public CMessageFilter, public CIdleHandler { public: enum { IDD = IDD_MAINDLG };
virtual BOOL PreTranslateMessage(MSG* pMsg); virtual BOOL OnIdle();
BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) COMMAND_ID_HANDLER_EX(IDC_ALYSON_BTN, OnAlysonODBtn) END_MSG_MAP()
BEGIN_UPDATE_UI_MAP(CMainDlg) END_UPDATE_UI_MAP() //... }; |
注意CMainDlg类从CUpdateUI派生并含有一个update UI链。OnInitDialog()做了这些工作,这和前面介绍的框架窗口中的代码很相似:
// register object for message filtering and idle updates CMessageLoop* pLoop = _Module.GetMessageLoop(); ATLASSERT(pLoop != NULL); pLoop->AddMessageFilter(this); pLoop->AddIdleHandler(this);
UIAddChildWindowContainer(m_hWnd); |
只是这次我们不是调用UIAddToolbar()或UIAddStatusBar(),而是调用UIAddChildWindowContainer(),它告诉CUpdateUI我们的对话框含有需要updating的字窗口,只要看看OnIdle(),你会怀疑少了写什么:
BOOL CMainDlg::OnIdle() { return FALSE; } |
你可能猜想这里应该调用另一个CUpdateUI的方法做一些实在的updating工作,你是对的,应该是这样的,向导在OnIdle()中漏掉了一行代码,现在加上:
BOOL CMainDlg::OnIdle() { UIUpdateChildWindows(); return FALSE; } |
为了演示UI updating,我们设定鼠标点击左边的位图按钮,使得右边的按钮变得可用或禁用。先在update UI链中添加一个消息入口,使用UPDUI_CHILDWINDOW标志表示此入口是子窗口类型:
BEGIN_UPDATE_UI_MAP(CMainDlg) UPDATE_ELEMENT(IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW) END_UPDATE_UI_MAP() |
在左边的按钮的单击事件处理中,我们调用UIEnable()来翻转另一个按钮的使能状态:
void CMainDlg::OnAlysonODBtn ( UINT uCode, int nID, HWND hwndCtrl ) { static bool s_bBtnEnabled = true;
s_bBtnEnabled = !s_bBtnEnabled; UIEnable ( IDC_ALYSON_BMPBTN, s_bBtnEnabled ); } |
DDV
WTL的对话框数据验证(DDV)比MFC简单一些,在MFC中你需要分别使用DDX(对话框数据交换)宏和DDV(对话框数据验证)宏,在WTL中只需一个宏就可以了,WTL包含基本的数据验证支持,在DDV链中可以使用三个宏:
DDX_TEXT_LEN
和DDX_TEXT一样,只是还要验证字符串的长度(不包含结尾的空字符)小于或等于限制长度。
DDX_INT_RANGE and DDX_UINT_RANGE
和DDX_INT,DDX_UINT一样,还加了对数字的最大最小值的验证。
DDX_FLOAT_RANGE
除了像DDX_FLOAT一样完成数据交换之外,还验证数字的最大最小值。
ControlMania2有一个ID是IDC_FAV_SEASON的edit box,它和成员变量m_nSeason相关联。
由于有效的值是1到7,所以使用这样的数据验证宏:
BEGIN_DDX_MAP(CMainDlg) //... DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, 1, 7) END_DDX_MAP() |
OnOK()调用DoDataExchange()获得season的数值,并验证是在1到7之间。
|