关键字:CFileDialog扩展类 ,OFNHookProc
需求描述 : 昨天遇到一个用户需求,在CFileDialog对话框(SAVE)中加入一个Check , 一个Edit, 一个Spin控件,dlg.Domodal()==IDOK返回时要同时获得Edit控件中的数据.
实现方法: 如果只需要控制控件,用方法一即可,但如果需要获取数据,则只能用方法二。我最终用的就是方法二.
方法一: 首先查找MSDN关于CFileDialog扩展的实现描述,上网查到实现预览图片式的CFILEDIALOG的例子。按照其思路从CFileDialog派生了一个新的类CFileDialogEX.
在类中添加以下代码
#define WM_CHECKZOOMIN WM_USER+1856
public:
BOOL bZoomIn;
UINT nMul; //[1~100]
//前置声明---------------
BOOL NEAR CALLBACK HandleNotify(HWND hDlg, LPOFNOTIFY pofn);
UINT_PTR CALLBACK OFNHookProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
// CFileDialogEx
IMPLEMENT_DYNAMIC(CFileDialogEx, CFileDialog)
CFileDialogEx::CFileDialogEx(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName,
DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) :
CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd)
{
//这个四个常用标识位
m_ofn.Flags |= OFN_ENABLETEMPLATE |OFN_HIDEREADONLY |OFN_EXPLORER; //OFN_EXPLORER | OFN_ENABLEHOOK
m_ofn.lpstrTitle = _T("高级保存对话框");
m_ofn.hInstance = AfxGetInstanceHandle();
m_ofn.lpstrFilter = _T("*.bmp\0*.BMP\0所有文件 \0 *.*\0 ");
m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_EX);
m_ofn.lpfnHook = (LPOFNHOOKPROC)OFNHookProc;
}
//Hook function for the Comm Dlg
//在这里处理我们感兴趣的消息,想要哪个,截哪个!
UINT_PTR CALLBACK OFNHookProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG: //初始化对话框上的控件
if(SendDlgItemMessage(hDlg, IDC_CHECK_ZOOMIN, BM_GETCHECK,0,0) == BST_UNCHECKED)
{
::EnableWindow(GetDlgItem(hDlg,IDC_EDIT_ZOOMNUM),FALSE);
::EnableWindow(GetDlgItem(hDlg,IDC_SPIN_ZOOMNUM),FALSE);
::SetWindowText(GetDlgItem(hDlg, IDC_EDIT_ZOOMNUM),_T("1"));
//ShowWindow(button,SW_HIDE);
}
break;
case WM_DESTROY: //消毁对话框
{
LPOPENFILENAME lpOFN = (LPOPENFILENAME)GetWindowLong(hDlg, DWL_USER);
}
break;
case WM_COMMAND: //这里处理,IDC_CHECK_ZOOMIN命令
{
if ((HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_CHECK_ZOOMIN))
{
BOOL bZoomIn = TRUE;
if(SendDlgItemMessage(hDlg, IDC_CHECK_ZOOMIN, BM_GETCHECK, 0, 0) == BST_CHECKED)
{
bZoomIn = TRUE;
::EnableWindow(GetDlgItem(hDlg,IDC_EDIT_ZOOMNUM),TRUE);
::EnableWindow(GetDlgItem(hDlg,IDC_SPIN_ZOOMNUM),TRUE);
}
else
{
bZoomIn = FALSE;
//::SetWindowText(GetDlgItem(hDlg, IDC_EDIT_ZOOMNUM),"");
::EnableWindow(GetDlgItem(hDlg,IDC_EDIT_ZOOMNUM),FALSE);
::EnableWindow(GetDlgItem(hDlg,IDC_SPIN_ZOOMNUM),FALSE);
}
HWND hParent = GetParent(hDlg); // 注意hDlg是新加的模板对话框句柄,它是CFileDialog对话框的子部分,要获取CFileDialog对话框的句柄需要用GetParent函数
PostMessage(hParent,WM_CHECKZOOMIN,(WPARAM)bZoomIn,(LPARAM)0);
bZoomIn = FALSE;
}
break;
case WM_NOTIFY:
HandleNotify(hDlg, (LPOFNOTIFY)lParam);
break;
}
return 0;
}
//这里处理notify 消息
BOOL NEAR CALLBACK HandleNotify(HWND hDlg, LPOFNOTIFY pofn)
{
CFileDialogEx dlg(TRUE);
switch (pofn->hdr.code)
{
case CDN_SELCHANGE:
{
//char szFile[MAX_PATH];
//// Get the path of the selected file.
//if (CommDlg_OpenSave_GetFilePath(GetParent(hDlg), szFile, sizeof(szFile)) <= sizeof(szFile))
//{
// if(GetFileAttributes(szFile) != FILE_ATTRIBUTE_DIRECTORY)
// {
// //Should we load the Pic
// if(SendDlgItemMessage(hDlg, IDC_CHECK1, BM_GETCHECK,0,0) == BST_UNCHECKED)
// dlg.ShowImagepreview(hDlg, szFile);
// }
//}
}
break;
case CDN_FILEOK:
{
int num;
GetDlgItemInt(hDlg,IDC_EDIT_ZOOMNUM,&num,FALSE);
if(SendDlgItemMessage(hDlg, IDC_CHECK_ZOOMIN, BM_GETCHECK, 0, 0) == BST_CHECKED)
{
dlg.bZoomIn = TRUE;
}
else
dlg.bZoomIn = FALSE;
return FALSE;
}
break;
}
return(TRUE);
}
原计划从回调函数得到触发消息,然后用PostMessage的方式将自定义的WM_CHECKZOOMIN消息发给主窗口,在主窗口中更新我新加的变量bZoomIn和
nMul的值。
但调试发现我新定义的CFileDialogEX类中的WM_CHECKZOOMIN的消息响应函数总是无法进入。进一步调式确定消息确实是发送成功了,而且窗口句柄也没有错,但就是进入不了我定义的响应函数。 经过资料查询和思索初步推理: CFileDialog的消息处理是封闭的,自动的,在没有用钩子的情况下我们是无法将自己的消息函数插入其中并能自己处理的。 从MSDN给的说明也可以看出,它推荐PostMessage传输的也只是系统定义消息如WM_COMMAND,也就是说你可以控制它的控件做某项动作或显示隐藏控件,移动控件的位置。
一切回到了原点,此路不通,继续寻找新的方法!!!!!!
方法二:看到一篇文章http://www.softist.com/programemo/cfiledialog/cfiledialog.htm,给我指了一条明路
CFileDialog的个性化(VC++)
本文是为标准的文件选择对话框添加控件以扩展它的功能。为了做到这点,要把CFileDialog类派生,还要准备粘贴控件用的对话框模板,把控件的消息响应入口追加到MESSAGE-MAP中。
例程。文件选择对话框在选择文本文件时,可以预览这个文件的内容。
1.生成对话框模板。作一个对话框的资源,比如ID为IDD_DIALOG1。其属性如下设定。
IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 187, 128 STYLE WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN FONT 9, "MS Sans Serif" BEGIN LTEXT "Static",stc32,7,7,173,114 EDITTEXT IDC_EDIT_PREVIEW,7,18,102,30,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN PUSHBUTTON "Button1",IDC_BUTTON_PREVIEW,143,36,37,23 END
2.追加控件。首先追加一个ID为stc32的大一点的静态文字控件(Static)。然后在它的下面,追加预览用的多行编辑框(ID=IDC_EDIT_PREVIEW)和起动预览用的按钮(ID=IDC_BUTTON_PREVIEW)。
3.生成CFileDialog的派生类,比如叫CFileDlg。为了指定资源ID,追加如下的代码到CFileDlg里。这个追加还有一个意义,那就是可以用MFC的类精灵工具(MFC ClassWizard)来作相应的处理。
// Dialog Data
//{{AFX_DATA(CFileDlg)
enum { IDD = IDD_DIALOG1 };
//}}AFX_DATA
4.把上面生成的对话框模板,安装到CFileDlg的类创建函数里,代码如下。
CFileDlg::CFileDlg(BOOL bOpenFileDialog, ...) : CFileDialog(bOpenFileDialog, ...)
{
m_ofn.Flags |= dwFlags;
m_ofn.Flags |= OFN_EXPLORER;
m_ofn.Flags |= OFN_ENABLETEMPLATE;
SetTemplate(0, IDD_DIALOG1);
}
5.填写按钮(ID=IDC_BUTTON_PREVIEW)的响应函数。从CFileDlg类的实体对话框取得文件的全路径,打开文件,把其内容显示在编辑框(ID=IDC_EDIT_PREVIEW)上。
void CFileDlg::OnPreview()
{
CString strPathName = GetPathName();
if (strPathName != "")
{
CFile file;
file.Open(strPathName, CFile::modeRead);
char buff[1024];
memset(buff, 0, 1024);
file.Read(buff, 1024);
GetDlgItem(IDC_EDIT_PREVIEW)->SetWindowText(buff);
file.Close();
}
}
6.结果测试。
CFileDlg dlg(TRUE);
dlg.DoModal();
通过指定资源ID , 可以使用Wizard添加处理消息, OK 这正是我想要的, 按照它的方法,很快就实现了需求,调用如下
//.h
// Dialog Data
//{{AFX_DATA(CFileDlg)
enum { IDD = IDD_FILEDIALOG_EX };
//}}AFX_DATA
public:
BOOL bZoomIn;
UINT nMul; //[1~100]
afx_msg void OnBnClickedCheckZoomin();
afx_msg void OnDeltaposSpinZoomnum(NMHDR *pNMHDR, LRESULT *pResult);
virtual BOOL OnInitDialog();
afx_msg void OnEnChangeEditZoomnum();
afx_msg void OnEnKillfocusEditZoomnum();
//.cpp
CFileDialogEx::CFileDialogEx(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName,
DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) :
CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd)
{
//这个四个常用标识位
m_ofn.Flags |= OFN_ENABLETEMPLATE |OFN_HIDEREADONLY |OFN_EXPLORER; //OFN_EXPLORER | OFN_ENABLEHOOK
m_ofn.lpstrTitle = _T("高级保存对话框");
m_ofn.hInstance = AfxGetInstanceHandle();
m_ofn.lpstrFilter = _T("*.bmp\0*.BMP\0所有文件 \0 *.*\0 ");
SetTemplate(0, IDD_FILEDIALOG_EX);
}
BEGIN_MESSAGE_MAP(CFileDialogEx, CFileDialog)
//ON_COMMAND()
ON_BN_CLICKED(IDC_CHECK_ZOOMIN, &CFileDialogEx::OnBnClickedCheckZoomin)
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ZOOMNUM, &CFileDialogEx::OnDeltaposSpinZoomnum)
ON_EN_CHANGE(IDC_EDIT_ZOOMNUM, &CFileDialogEx::OnEnChangeEditZoomnum)
ON_EN_KILLFOCUS(IDC_EDIT_ZOOMNUM, &CFileDialogEx::OnEnKillfocusEditZoomnum)
END_MESSAGE_MAP()
void CFileDialogEx::OnBnClickedCheckZoomin()
{
// TODO: 在此添加控件通知处理程序代码
bZoomIn = !bZoomIn;
GetDlgItem(IDC_EDIT_ZOOMNUM)->EnableWindow(bZoomIn);
GetDlgItem(IDC_SPIN_ZOOMNUM)->EnableWindow(bZoomIn);
}
void CFileDialogEx::OnDeltaposSpinZoomnum(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
if (pNMUpDown->iDelta > 0)
{
if (nMul > 1)
nMul--;
}
else
{
if (nMul <= 100)
nMul++;
}
wchar_t buff[10];
_itow( nMul, buff, 10 );
LPCTSTR lpstr = (LPCTSTR)buff;
GetDlgItem(IDC_EDIT_ZOOMNUM)->SetWindowText(lpstr);
*pResult = 0;
}
BOOL CFileDialogEx::OnInitDialog()
{
CFileDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
GetDlgItem(IDC_EDIT_ZOOMNUM)->EnableWindow(FALSE);
GetDlgItem(IDC_SPIN_ZOOMNUM)->EnableWindow(FALSE);
GetDlgItem(IDC_EDIT_ZOOMNUM)->SetWindowText(_T("1"));
bZoomIn = FALSE;
nMul = 1;
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
void CFileDialogEx::OnEnChangeEditZoomnum()
{
// TODO: 如果该控件是 RICHEDIT 控件,它将不
// 发送此通知,除非重写 CFileDialog::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
// TODO: 在此添加控件通知处理程序代码
CString strText;
GetDlgItem(IDC_EDIT_ZOOMNUM)->GetWindowText(strText);
nMul = _wtoi(strText);
if ( (nMul < 1) || (nMul > 100) )
{
if (nMul < 1) nMul = 1;
if (nMul > 100) nMul = 100;
wchar_t buff[10];
_itow( nMul, buff, 10 );
LPCTSTR lpstr = (LPCTSTR)buff;
GetDlgItem(IDC_EDIT_ZOOMNUM)->SetWindowText(lpstr);
}
}
void CFileDialogEx::OnEnKillfocusEditZoomnum()
{
// TODO: 在此添加控件通知处理程序代码
CString strText;
GetDlgItem(IDC_EDIT_ZOOMNUM)->GetWindowText(strText);
nMul = _wtoi(strText);
}
// 调用实现代码
CString szFilter1 = _T("保存图象文件(*.bmp)|*.bmp;|所有文件(*.*)|*.*||");
CFileDialogEx dlg(FALSE, _T(".bmp"), NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,szFilter1);
if (dlg.DoModal()==IDOK)
{
CComBSTR str=dlg.GetPathName();
if (!dlg.bZoomIn)
{ }
else
{ float Magnification = dlg.nMul;}
}