WTL檔案選擇對話方塊 - CFileDialogImpl
1)首先下面的代碼是直接使用Win32 API來開啟單個檔案的代碼:
void WinSDK_OpenFileDemo()
{
OPENFILENAME ofn = {0};
char szFile[MAX_PATH];
*szFile = 0;
// 開始設定OPENFILENAME結構成員
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.lpstrFile = szFile;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = "ESRI Shape檔案(*.SHP)/0*.shp/0AutoCAD分頁檔(*.DXF;*.DXF.GZ)/0*.dxf;*.dxf.gz /0所有影像檔/0*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tif;*.tiff/0所有檔案(*.*)/0*.*/0/0";
ofn.nFilterIndex = 1;
ofn.lpstrTitle = "開啟地圖檔案";
ofn.lpstrInitialDir = 0;
ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
// 指派給"開啟"對話方塊, 並顯示對話方塊
if (::GetOpenFileName(&ofn))
{
// 現在下面的成員中存放了我們要的檔案名稱
// ofn.lpstrFile;
}
}
2)類似的用WTL來實現檔案開啟的功能就簡單多了。下面的代碼同時開啟多個檔案:
LRESULT CMainFrame::OnFileOpen(...)
{
CMyFileDialog openFileDlg( TRUE,
_T("shp"),
_T(""),
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT,
_T("Shape 檔案(*.shp)|*.shp|CAD分頁檔(*.dxf)|*.dxf|影像檔|*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tif;*.tiff|所有檔案(*.*)|*.*|"),
this->m_hWnd,
_T("開啟地圖檔案")
);
if ( IDOK == openFileDlg.DoModal() )
{
int nOpenFiles = openFileDlg.GetOpenFileCount();
if (nOpenFiles>0)
{
for (int i=0; i<nOpenFiles; i++)
{
//檔案名稱:openFileDlg.GetOpenFileAt(i);
}
}
}
return 0;
}
3)完整的CMyFileDialog的代碼如下(在WTL8.0, 8.1測試通過):
class CMyFileDialog :
public CFileDialogImpl<CMyFileDialog>
{
public:
// Construction
CMyFileDialog (BOOL bOpenFileDialog,
_U_STRINGorID szDefExt = 0U,
_U_STRINGorID szFileName = 0U,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_U_STRINGorID szFilter = 0U,
HWND hwndParent = NULL,
_U_STRINGorID szTitle= 0U ) :
CFileDialogImpl<CMyFileDialog>(bOpenFileDialog, NULL, NULL, dwFlags, NULL, hwndParent),
m_sDefExt(szDefExt.m_lpstr),
m_sFileName(szFileName.m_lpstr),
m_sFilter(szFilter.m_lpstr),
m_sTitle(szTitle.m_lpstr),
_FilesCount(-1)
{
m_ofn.lpstrDefExt = m_sDefExt;
m_ofn.lpstrFilter = PrepFilterString ( m_sFilter );
m_ofn.lpstrTitle = m_sTitle;
// setup initial file name
if ( !m_sFileName.IsEmpty() )
lstrcpyn ( m_szFileName, m_sFileName, _MAX_PATH );
}
// Maps
BEGIN_MSG_MAP(CMyFileDialog)
CHAIN_MSG_MAP(CFileDialogImpl<CMyFileDialog>)
END_MSG_MAP()
// Overrides
void OnInitDone ( LPOFNOTIFY lpon )
{
GetFileDialogWindow().CenterWindow(lpon->lpOFN->hwndOwner);
}
// Get Open Files Count
int GetOpenFileCount(void)
{
if (!m_bOpenFileDialog)
return 0;
if (_FilesCount==-1)
{
int i = 0;
int i0 = 0;
_FilesCount = 0;
while(i < _MAX_PATH-1){
if (m_szFileName[i]==0){
_FileStarts[_FilesCount++] = i0;
i0 = i+1;
if (m_szFileName[i+1]==0)
break;
}
i++;
}
if (_FilesCount>1){
_tcsncpy_s(_FileName, _MAX_PATH, m_szFileName, _FileStarts[1]);
_FileName[ _FileStarts[1] ] = 0;
_FileName[ _FileStarts[1]-1 ] = _T('//');
_FilesCount--;
}
}
return _FilesCount;
}
// Get Open FileName: index 0-based
LPCTSTR GetOpenFileAt(int index)
{
ATLASSERT(_FilesCount > 0);
if (_FilesCount==1){
ATLASSERT(index==0);
return m_szFileName;
}
_FileName[ _FileStarts[1] ] = 0;
_tcscat_s(_FileName, _MAX_PATH, m_szFileName + _FileStarts[index+1]);
return _FileName;
}
protected:
LPCTSTR PrepFilterString ( CString& sFilter )
{
LPTSTR psz = sFilter.GetBuffer(0);
LPCTSTR pszRet = psz;
while ( '/0' != *psz )
{
if ( '|' == *psz )
*psz++ = '/0';
else
psz = CharNext ( psz );
}
return pszRet;
}
int _FilesCount;
int _FileStarts[_MAX_PATH];
TCHAR _FileName[_MAX_PATH];
CString m_sDefExt, m_sFileName, m_sFilter, m_sTitle;
};
4)結論
編寫windows用戶端介面程式有不少於100種方法可用。經典的如MFC;時髦的如DHTML,Form,WPF;純理論的如SDK;跨平台的如Java(這裡面就有N多架構),QT等等。可是至今我覺得還是WTL/ATL最乾淨利落,也最強大。尤其WTL,雖然不是MS力推的主流,但這主要是因為MS為了.NET平台推廣而有意壓制的技術。WTL也是最適合我們玩的東西。