7.4.1 “打开”文件和“保存”文件对话框
请先看下面一段用于显示“打开”文件对话框的代码(07CommDlg工程):
char szFileName[MAX_PATH] = "";
OPENFILENAME file = { 0 };
file.lStructSize = sizeof(file); // 设置对话框的属性
file.lpstrFile = szFileName;
file.nMaxFile = MAX_PATH;
file.lpstrFilter = "Text Files(*.txt)\0*.txt\0All Files\0*.*\0\0";
file.nFilterIndex = 1; // 弹出打开文件的对话框
if(::GetOpenFileName(&file))
{
::MessageBox(NULL, szFileName, "07CommDlg", MB_OK);
}
程序运行效果如图7.9所示。选择一个文件后,系统将弹出一个对话框(MessageBox)显示所选择文件的名称(包含路径)。
显示“打开”文件对话框的函数是GetOpenFileName,显示“保存”文件对话框的函数是GetSaveFileName。这两个对话框可以让用户选择驱动器、目录以及文件名,但它们并不对文件进行任何操作。函数用法如下。
BOOL GetOpenFileName ( LPOPENFILENAME lpofn);
BOOL GetSaveFileName ( LPOPENFILENAME lpofn);
lpofn是一个指向OPENFILENAME结构的指针,程序在调用函数前需要在结构中填写初始化数据。两个函数使用的结构是一样的,只是使用的初始化数据有所不同而已。OPENFILENAME结构的定义如下。
typedef struct tagOFN
{
DWORD lStructSize; // 结构的长度,用户填写
HWND hwndOwner; // 所属窗口
HINSTANCE hInstance; // 所在程序的实例句柄
LPCTSTR lpstrFilter; // 文件筛选字符串
LPTSTR lpstrCustomFilter;
DWORD nMaxCustFilter;
DWORD nFilterIndex;
LPTSTR lpstrFile; // 全路径的文件名缓冲区
DWORD nMaxFile; // 全文件名缓冲区长度
LPTSTR lpstrFileTitle; // 不包含路径的文件名缓冲区
DWORD nMaxFileTitle; // 文件名缓冲区长度
LPCTSTR lpstrInitialDir; // 初始目录
LPCTSTR lpstrTitle; // 对话框标题
DWORD Flags; // 标志
WORD nFileOffset; // 文件名在字符串中的起始位置
WORD nFileExtension; // 扩展名在字符串中的起始位置
LPCTSTR lpstrDefExt; // 默认扩展名
LPARAM lCustData;
LPOFNHOOKPROC lpfnHook;
LPCTSTR lpTemplateName;
} OPENFILENAME, *LPOPENFILENAME;
下面是结构中一些重要字段的含义。
(1)lpstrFilter,指定文件名筛选字符串,该字段决定了对话框中“文件类型”下拉式列表框中的内容,字符串可以由多组内容组成,每组包含一个说明字符串和一个筛选字符串,字符串的最后用两个0结束。例如,加上下面的代码后显示结果如图7.10所示。
file.lpstrFilter = "Text Files(*.txt)\0*.txt\0All Files\0*.*\0\0";
file.nFilterIndex = 1; // 默认选择第一个
(2)lpstrInitialDir,对话框的初始化目录,这个字段可以是NULL。
(3)lpstrDefExt,指定默认扩展名,如果用户输入了一个没有扩展名的文件名,那么函数会自动加上这个默认扩展名。
(4)Flags,该字段决定了对话框的不同行为。它可以是一些标志的组合。下面是几个比较重要的标志:
OFN_ALLOWMULTISELECT 允许同时选择多个文件
OFN_CREATEPROMPT 如果用户输入了一个不存在的文件名,对话框向用户询问“是否建 立新文件”
OFN_OVERWRITEPROMPT 在“保存”文件对话框中使用的时候,当用户选择的文件存在时, 对话框会提问“是否覆盖文件”。
如果用户单击了对话框中的“确定”按钮,函数返回TRUE;如果单击了“取消”按钮,函数返回FALSE。
7.4.2 浏览目录对话框
显示浏览目录对话框的功能由SHBrowseForFolder函数实现,函数的用法如下。
LPITEMIDLIST SHBrowseForFolder(LPBROWSEINFO lpbi);
参数lpbi指向包含对话框初始数据的BROWSEINFO结构。
typedef struct _browseinfo
{
HWND hwndOwner; // 对话框的父窗口
LPCITEMIDLIST pidlRoot; // 用来表示起始目录的ITEMIDLIST结构
LPTSTR pszDisplayName; // 用来接受用户选择目录的缓冲区
LPCTSTR lpszTitle; // 对话框中用户定义的文字
UINT ulFlags; // 标志
BFFCALLBACK lpfn; // 回调函数地址
LPARAM lParam; // 传给回调函数的参数
int iImage; // 用来接受选中目录的图像
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
ulFlags字段用来定义对话框的风格,可以取值如下:
BIF_BROWSEINCLUDEFILES 可同时显示目录中的文件
BIF_RETURNONLYFSDIRS 只返回文件系统中的目录
BIF_EDITBOX 显示一个编辑框供用户输入目录
BIF_EDITBOX 显示一个编辑框供用户输入目录
当该函数返回时,如果用户单击的是“取消”按钮,那么函数的返回值是0,否则,函数返回指向ITEMIDLIST结构的指针,对于这个结构可以不必去深究,因为使用SHGetPathFromIDList函数可以很方便地将它转化成目录字符串。
BOOL SHGetPathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath);
函数执行后,字符串格式的用户选择的目录名将会返回到pszPath所指的缓冲区中。
为了今后使用方便,下面简单封装了一个CDirDialog类用于管理浏览目录对话框。这个类定义和实现代码都在DirDialog.h文件中。
// DirDialog.h文件
#ifndef __DIRDIALOG_H_
#define __DIRDIALOG_H_
#include <shlobj.h>
class CDirDialog
{
public:
CDirDialog();
// 显示对话框
BOOL DoBrowse(HWND hWndParent, LPCTSTR pszTitle = NULL);
// 取得用户选择的目录名称
LPCTSTR GetPath() { return m_szPath; }
protected:
BROWSEINFOA m_bi;
// 用来接受用户选择目录的缓冲区
char m_szDisplay[MAX_PATH];
char m_szPath[MAX_PATH];
};
CDirDialog::CDirDialog()
{
memset(&m_bi, 0, sizeof(m_bi));
m_bi.hwndOwner = NULL;
m_bi.pidlRoot = NULL;
m_bi.pszDisplayName = m_szDisplay;
m_bi.lpszTitle = NULL;
m_bi.ulFlags = BIF_RETURNONLYFSDIRS;
m_szPath[0] = '\0';
}
BOOL CDirDialog::DoBrowse(HWND hWndParent, LPCTSTR pszTitle)
{
if(pszTitle == NULL)
m_bi.lpszTitle = "选择目标文件夹";
else
m_bi.lpszTitle = pszTitle;
m_bi.hwndOwner = hWndParent;
LPITEMIDLIST pItem = ::SHBrowseForFolder(&m_bi);
if(pItem != 0)
{
::SHGetPathFromIDList(pItem, m_szPath);
return TRUE;
}
return FALSE;
}
#endif //__DIRDIALOG_H_
类的用法相当简单,例如下面的代码将打开一个浏览目录对话框。
CDirDialog dir;
if(dir.DoBrowse(hDlg)) // hDlg是父窗口句柄
{
::MessageBox(hDlg, dir.GetPath(), "07CommDlg", MB_OK);
}
代码执行之后的效果如图7.11所示。