WEB开发网
开发学院软件开发VC 一个定制CFileDialog对话框的实例 阅读

一个定制CFileDialog对话框的实例

 2010-06-27 20:41:55 来源:WEB开发网   
核心提示:当所选目录中没有.txt文件时,要disable“全部”按钮的处理稍微麻烦一些,一个定制CFileDialog对话框的实例(2),要用到ON_UPDATE_COMMAND_UI消息,回顾一下MFC有关UI更新的基本方法,这些符号都定义在文件中,你可以把列表控制看成是lst1,通常是在主消息循环处

当所选目录中没有.txt文件时,要disable“全部”按钮的处理稍微麻烦一些,要用到ON_UPDATE_COMMAND_UI消息。回顾一下MFC有关UI更新的基本方法,通常是在主消息循环处于空闲状态时候——也就是说在消息队列中没有待处理的消息。但对话框则有所不同,尤其是运行模式对话框时,MFC启动另外一个消息循环。当没有消息等待处理的时候,CWnd::DoModal向对话框发送一个WM_KICKIDLE消息。所以要想让对话框处理UI,常用的方式是这样的:

LRESULT CMyDialog::OnKickIdle(WPARAM wp, LPARAM lp)
{
 UpdateDialogControls(this, TRUE);
 return 0;
}   

CWnd::UpdateDialogControls将神奇的CN_UPDATE_COMMAND_UI消息发送到对话框,触发ON_UPDATE_COMMAND_UI处理例程。可惜这个方法对CFileDialog对话框不灵。原因是CFileDialog重写了DoModal,它不会以正常方式运行某个消息循环,而是调用::GetOpenFileName (或::GetSaveFileName)。这些API函数都有自己消息循环,并且你无法钻进去进行消息空闲处理。无论什么时候,每当模式对话框处于等待消息状态时,对话框发送自己的WM_ENTERIDLE消息。从这里进去才可以处理UI更新事宜。但有几个细节需要注意。首先,Windows只发送WM_ENTERIDLE消息到对话框的所有者——此处为主框架——所以必须在那里捕获这个消息。然后,只要对话框仍然处于空闲状态,则Windows继续发送WM_ENTERIDLE,但只需要调用UpdateDialogControls一次,此间可以进行常规的标志设置。那到底什么时候设置标志呢?无论何时,UI状态的改变,都是在对话框获得到WM_COMMAND 或 WM_NOTIFY消息之后。所以还必须在CFileDialog派生的对话框中截获这些消息。因为这些都是一些繁琐的细节,所以最好将它们封装到在一个新类中,这就是CFileDlgHelper的来由。只要从CFileDialog派生的对话框OnInitDialog函数中调用CFileDlgHelper的Init,便不用操心ON_UPDATE_COMMAND_UI的处理细节。CFileDlgHelper是如何实现的呢?告诉你吧,利用万能类CSubclassWnd,这个类可以用Windows的方式子类化任何窗口,通过在某个窗口过程之前安装一个新的窗口过程来实现消息的捕获。实际上,CFileDlgHelper 用了两个CSubclassWnds派生类:一个用来截获发送到对话框父窗口的WM_ENTERIDLE消息,另一个用来截获发送到对话框本身的WM_COMMAND 或 WM_NOTIFY。当主窗口得到WM_ENTERIDLE消息时,CFileDialogOwnerHook解释它并更新对话框控制:

LRESULT CFileDialogOwnerHook::WindowProc(...)
{
 if (msg==WM_ENTERIDLE) {
  if (m_pHelper->m_bUpdateUI) {
   m_pDlg->UpdateDialogControls(m_pDlg, FALSE);
   m_pHelper->m_bUpdateUI=FALSE;
  }
 }
 return CSubclassWnd::WindowProc(msg, wp, lp);
}

当对话框得到WM_NOTIFY 或者WM_COMMAND消息时,CFileDialogHook重置标志。

LRESULT CFileDialogHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
 if (msg==WM_COMMAND || msg==WM_NOTIFY) {
  m_pHelper->m_bUpdateUI = TRUE;
 }
 return CSubclassWnd::WindowProc(msg, wp, lp);
}  

一旦知道了其中的奥秘,一切就这么简单。注意从CSubclassWnd派生了两个类——CFileDialogOwnerHook和CFileDialogHook,一个用来对付主框架,另一个用来对付对话框本身,它们都在隐含在CFileDlgHelper类中。有了它,“按钮”的UI更新就会象你所期望的那样:

void CMyOpenDlg::OnUpdateSelectAll(CCmdUI* pCmdUI)
{
 CFileDlgHelper& fdh = m_dlghelper;
 CListCtrl* plc = fdh.GetListCtrl();
 for (int i=0; i<plc->GetItemCount(); i++) {
  if (IsTextFileName(fdh.GetItemName(i))) {
   pCmdUI->Enable(TRUE);
   return;
  }
 }
 pCmdUI->Enable(FALSE);
}

以上是用户需求的实现,下面来解决Window 2000环境测试出现的问题:如何确定在列表框中选择的是文件还是文件夹。

要想解决这个问题,就必须关注对话框中的列表控制(ListCtrl/ListView),许多普通的对话框里的控制都有明确的IDs,如静态文本控制有stc1,以及列表框有lst1,这些符号都定义在文件中。你可以把列表控制看成是lst1,但用Spy++察看后,如图一所示:

图一 Spy++

Tags:一个 定制 CFileDialog

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接