转自:http://www.vckbase.com/index.php/wv/228

很多程序员都喜欢让自己的代码运行效果与众不同。Windows系统的应用程序打开某个文件一般使用的都是默认的CFileDialog。但是这个默认的CFileDialog往往满足不了用户的要求。我就碰到一个这样的用户,他的要求如下:

1、在默认的CFileDialog对话框中加一个预览窗格,以便在选中ASCII文件时能看到所选文件的内容,也就是用*.txt作为文件过滤条件。

2、在默认的CFileDialog对话框中加一个"全部"按钮来选择某个目录中所有的.txt文件。

3、如果选择的目录中没有.txt文件时,要将"全部"按钮disable,也就是置灰这个按钮。

实现上面这些需求必须要改装CFileDialog对话框。当最后写完程序时,功能到是全都实现了,但在Windows 2000环境测试中,用户发现了这样一个问题:如果先选中某个文件,然后再去选某个文件夹,预览窗格仍然显示的那个文件的内容。尽管在OnFileNameChange中对CDN_SELCHANGE进行了处理,为了获取所选的文件/路径名,也调用了CFileDialog::GetPathName。但是即使是选中了文件夹,GetPathName仍然返回的是文件的名字。即便尝试用其它的通知消息和函数,比如 CDN_FOLDERCHANGE 和 GetFileName,但仍旧存在同样的问题。必须承认在Windows 2000中,CFileDialog是个不完美的对话框,确实存在上述问题。正是有这些不完美,程序员们才忙得个不亦乐乎......那么到底如何判断用户选中的是文件还是文件夹呢?下面就让我们从用户需求开始,一个一个解决所碰到的问题。

首先简单介绍下本文引入的三个辅助类:CFileDialogHook;CFileDialogOwnerHook和CFileDlgHelper,这三个类很简单,其功能分别是:子类化文件对话框;子类化文件对话框的父窗口或宿主窗口,这两个类只在CFileDlgHelper类中使用,一些重要的处理都在CFileDlgHelper中。它的使用方法很简单,实例化CFileDlgHelper以后调用Init即可。

01. class CMyOpenDlg ... {
02. protected:
03. CFileDlgHelper m_dlghelper;//实例化
04. };
05. BOOL CMyOpenDlg::OnInitDialog()
06. {
07. m_dlghelper.Init(this)//初始化
08. ……
09. }

初始化CFileDlgHelper以后,便可以用它来获取列表控制以及判断选项是否有文件夹属性,例如:

01. CListCtrl* plc = m_dlghelper.GetListCtrl();
02. POSITION pos = plc->GetFirstSelectedItemPosition();
03. while (pos) {
04. int i = plc->GetNextSelectedItem(pos);
05. if (fdh.IsItemFolder(i)) {
06. // 显示"(FOLDER)"……
07. else {
08. // 显示其它内容
09. }
10. }

毫无疑问,要改装CFileDialog对话框,必须建立一个它的派生类以及一个新的对话框资源。“全部”按钮的实现代码是这样的:

01. void CMyOpenDlg::OnSelectAll()
02. {
03. CListCtrl* plc = m_dlghelper.GetListCtrl();
04. for (int i=0; iGetItemCount(); i++) {
05. CString fn = plc->GetItemText(i,0);
06. if (IsTextFileName(fn)) {
07. plc->SetItemState(i,LVIS_SELECTED,
08. LVIS_SELECTED);
09. }
10. }
11. plc->SetFocus();
12. }

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

1. LRESULT CMyDialog::OnKickIdle(WPARAM wp, LPARAM lp)
2. {
3. UpdateDialogControls(this, TRUE);
4. return 0;
5. }

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解释它并更新对话框控制:

01. LRESULT CFileDialogOwnerHook::WindowProc(...)
02. {
03. if (msg==WM_ENTERIDLE) {
04. if (m_pHelper->m_bUpdateUI) {
05. m_pDlg->UpdateDialogControls(m_pDlg, FALSE);
06. m_pHelper->m_bUpdateUI=FALSE;
07. }
08. }
09. return CSubclassWnd::WindowProc(msg, wp, lp);
10. }

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

1. LRESULT CFileDialogHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
2. {
3. if (msg==WM_COMMAND || msg==WM_NOTIFY) {
4. m_pHelper->m_bUpdateUI = TRUE;
5. }
6. return CSubclassWnd::WindowProc(msg, wp, lp);
7. }

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

01. void CMyOpenDlg::OnUpdateSelectAll(CCmdUI* pCmdUI)
02. {
03. CFileDlgHelper& fdh = m_dlghelper;
04. CListCtrl* plc = fdh.GetListCtrl();
05. for (int i=0; iGetItemCount(); i++) {
06. if (IsTextFileName(fdh.GetItemName(i))) {
07. pCmdUI->Enable(TRUE);
08. return;
09. }
10. }
11. pCmdUI->Enable(FALSE);
12. }

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

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

图一 Spy++

你会发现列表控制实际上被包含在另一个窗口类SHELLDLL_DefView中。SHELLDLL_DefView窗口的ID为lst2,其项下的列表控制(SysListView32)的子ID为1。所以,为了要得到这个列表控制,可以这样编码:

1. // 在自己的CFileDialog 派生类中
2. CListCtrl* plc = (CListCtrl*)GetParent()->GetDlgItem(lst2)->GetDlgItem(1);

记住,在定制CFileDialog时,它实际上是一个实际对话框的子对话框,这就是必须用GetParent的原因。更多的细节请参考MSDN中的相关文章。强制类型转换 CListCtrl* 与每一个常见的MFC诀窍一样,因为CListCtrl既没有数据成员也没有虚拟函数成员,它是一个纯粹的包装类(因为GetDlgItem返回一个临时的CWnd指针,而不是CListCtrl,每次碰到这种情况,常常都会让人感到沮丧,其实这很正常)。 一旦你有了列表控制的指针,便可以做任何想做事情――例如获取选中的路径名,调用CListCtrl::GetItemText并添加结果到当前打开的文件夹(GetFolderPath/CDM_GETFOLDERPATH)。有了路径名,如何知道它到底时文件还是文件夹呢?方法如下:

1. #include
2. // 检查路径名是不是文件夹
3. static BOOL IsFolder(LPCTSTR pathname)
4. {
5. struct stat st;
6. return stat(pathname, &st)==0 && (st.st_mode & _S_IFDIR);
7. }

这里需要注意的是:不管怎样,如果路径名不是文件夹,你也不能因此就断定它就是一个文件!因为它还可能是其它的外壳对象,如"网上邻居"或者"我的电脑"之类的东西。 详细做法可以参考本文的例子程序 OpenFileDlg,它还示范了如何建立预览对话框。这个程序可以进行多项选择,如果只选中一个.txt文件,则预览窗格显示文件的开始几行。程序还带一个调试窗口,窗口中列出选中的条目,如果选中的是文件夹,则在它的旁边会有“FOLDER”说明。如图二所示。

图二运行中的OpenFileDlg

如果选中的是文件夹,则OpenFileDlg会清空预览格,这样就解决了本文所提出的预览问题。当然,如果运行环境是Windows XP,而非Windows 2000,那么就不会碰上这个问题!在Windows XP中,OnFileNameChange/CDN_SELCHANGE会返回正确的文件名和文件夹名字。但仍然可以用CFileDlgHelper类获取列表控制,选项名称等。并且仍然需要IsFolder来检查路径名是不是文件夹。

其实,在OnSelectAll处理代码中,IsTextFileName的功能是查找以.txt结尾文件名字。这个函数真的能实现这个功能吗?其实,在程序中有个致命的问题――如果用户定制了资源管理器来隐藏已知文件类型的扩展名。那么,.txt就不会出现在列表框中。也就是说CFileDlgHelper::GetItemName返回foo,而不是foo.txt。实际上,如果扩展名被隐藏,那么象foo.txt、foo.jpg和foo.doc等等这样的文件都以名字foo出现(试一下就知道了)。如此一来,怎么知道这个foo文件到底是此foo,还是彼foo呢?问题真是解决不完啊,搞掂这个问题,又出那个问题。唉,好累啊,下次再说吧......

一个定制CFileDialog对话框的实例相关推荐

  1. cfiledialog对话框大小_一个定制CFileDialog对话框的实例

    很多程序员都喜欢让自己的代码运行效果与众不同.Windows系统的应用程序打开某个文件一般使用的都是默认的CFileDialog.但是这个默认的CFileDialog往往满足不了用户的要求.我就碰到一 ...

  2. cfiledialog对话框大小_XFileDialog-自定义的CFileDialog

    XFileDialog-自定义的CFileDialog 简介 被首次引入新的共同文件对话框时,我立刻喜欢他们期待的方式.但我也注意到,驱动器和目录下拉列表显得有点尴尬,特别是如果你想翻转两个不同的驱动 ...

  3. 关于 CFileDialog 对话框多选功能的一个问题

    From: http://www.vckbase.com/document/viewdoc/?id=1544 最近有位读者来信指出:<在线杂志>第26期中有一篇文章:"再谈 CF ...

  4. JavaHelp软件的一个定制实用程序类

    JavaHelp软件的一个定制实用程序类 发布者:IBM 日期: 2000-07-01 00:00:00 浏览次数:0 (共有_条评论) 查看评论 | 我要评论   级别: 初级 Marshall L ...

  5. cfiledialog对话框大小_利用MFC的CFileDialog生成Windows2000文件对话框 (转)

    利用MFC的CFileDialog生成Windows2000文件对话框 (转)[@more@] 利用MFC的CFileDialog生成windows2000文件对话框 周鸣扬 自Windows2000 ...

  6. wps应用程序实例_有一个WPS Office应用程序实例wps.exe在运行,请关闭后重试 。如何解决?...

    点击查看有一个WPS Office应用程序实例wps.exe在运行,请关闭后重试 .如何解决?具体信息 答:请按照下面步骤试试:右击任务栏-启动任务管理器-进程-结束进程树 1.右击系统下方的任务栏, ...

  7. web前端期末大作业 html+css+javascript 全球私人定制旅游网页设计实例(6个页面)

    web前端期末大作业 html+css+javascript 全球私人定制旅游网页设计实例(6个页面) 临近期末, 你还在为HTML网页设计结课作业,老师的作业要求感到头大?HTML网页作业无从下手? ...

  8. 一个完整的安装程序实例—艾泽拉斯之海洋女神出品(五) --补遗 (已补充第三部分完整版)

    转载时请务必保留转载出处和由艾泽拉斯之海洋女神出品的字样:如需刊登,请与作者联系.little_fairycat@126.com. 第三部分:其他  1. 修改显示界面的风格  Installshie ...

  9. mysql filde_备份一个约250G的mysql实例【xtrabackup备份方案对比】

    备份一个约250G的mysql实例 A. 使用xtrabackup备份加gzip单线程压缩 time innobackupex --user=test --password=test  --socke ...

最新文章

  1. transformer预测过程_2019最新进展 | Transformer在深度推荐系统中的应用
  2. all the input arrays must have same number of dimensions
  3. excel调用python编程-使用python集合进行EXCEL数据分析
  4. Android开发之百度地图在地图上绘画圆的方法(官方方法)
  5. mysql表连接_mysql表连接
  6. IntelliJ IDEA 显示行号方法
  7. 百度关键词排名查询源码_章丘百度霸屏总部,关键词排名腾沃云
  8. gcc -pthread_错误-在GCC Linux中使用C程序未定义对'pthread_create'的引用
  9. matlab2008设置子函数断点无效,程序不在断点处停止
  10. linux文档查看器翻译,mdv – Linux终端下的 Markdown 文档查看器
  11. java中hashmap_Java HashMap – Java中的HashMap
  12. 在PL/SQL中使用随机数和GUID
  13. 《51单片机应用开发从入门到精通》——2.8 用外部中断控制灯闪烁
  14. android View.getWidth() 和View.getHeight()返回0
  15. jquery 选择器 空格、大于、加号、波浪线区别
  16. 传感器基础结构与通信原理
  17. 计算机毕业论文指导过程记录表6,毕业论文指导记录表范文
  18. pyhook安装说明
  19. Unity分屏之使用TUIO实现互动投影
  20. STM32F407+Cubemx学习应用[5]——DMA收发ModbusRS232数据——威纶通触摸屏

热门文章

  1. #!/usr/bin/env node 到底是什么
  2. python docx文本替换保留样式
  3. DSM -- 软件安装
  4. 易文档(yidocs),更简单的markdown静态文档生成模板
  5. solrCloud配置
  6. 如何制作点餐小程序?
  7. 如何给pdf添加水印?
  8. 【图解数据结构与算法】视频教程正式上线B站,持续更新中......
  9. RTCP(一): RR--Receiver Reports 接收者报告
  10. 2021-07-30 es6 -第4章和String复习