在CMainFrame中定義變量:
CSplitterWnd m_wndSplitter;
BOOL CMainFrame::ReplaceView(int row, int col, CRuntimeClass *pViewClass, SIZE
size)
{
CCreateContext context;
BOOL bSetActive;
if ((this->m_wndSplitter.GetPane(row, col)->IsKindOf(pViewClass)) == TRUE)
return FALSE;
//獲取文檔對象的指針,以便在創建新視圖的過程中能夠使用它
CDocument *pDoc = ((CView*)m_wndSplitter.GetPane(row, col))->GetDocument();
CView *pActiveView = this->GetActiveView();
if (pActiveView == NULL || pActiveView == m_wndSplitter.GetPane(row, col))
bSetActive = TRUE;
else
bSetActive = FALSE;
pDoc->m_bAutoDelete = FALSE; //設置標誌,這樣當視圖銷燬時不會刪除文檔
((CView*)m_wndSplitter.GetPane(row, col))->DestroyWindow(); //刪除存在的視圖
pDoc->m_bAutoDelete = TRUE; //設回默認的標誌
//創建新視圖
context.m_pNewViewClass = pViewClass;
context.m_pCurrentDoc = pDoc;
context.m_pNewDocTemplate = NULL;
context.m_pLastView = NULL;
context.m_pCurrentFrame = NULL;
m_wndSplitter.CreateView(row, col, pViewClass, size, &context);
CView *pNewView = (CView*)m_wndSplitter.GetPane(row, col);
if (bSetActive == TRUE)
this->SetActiveView(pNewView);
m_wndSplitter.RecalcLayout(); //重新計算位置
// m_wndSplitter.GetPane(row,col)->SendMessage(WM_PAINT);
return TRUE;
}
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext)
{
if (!m_wndSplitter.CreateStatic(this, 1, 2))
return FALSE;
if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CTView), CSize(200, 100),
pContext))
return FALSE;
if (!m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CFView), CSize(100, 100),
pContext))
return FALSE;
m_bFormView = true;
return TRUE;
}
void CMainFrame::OnFormView()
{
ReplaceView(0, 1, RUNTIME_CLASS(CFView), CSize(100, 100));
m_bFormView = true;
}
void CMainFrame::OnListView()
{
ReplaceView(0, 1, RUNTIME_CLASS(CVVView), CSize(100, 100));
m_bFormView = false;
}
void CMainFrame::OnUpdateFormView(CCmdUI *pCmdUI)
{
pCmdUI->SetCheck(m_bFormView);
}
void CMainFrame::OnUpdateListView(CCmdUI *pCmdUI)
{
pCmdUI->SetCheck(!m_bFormView);
}
***********************************************************
下面我談談怎麼編寫這個程序:
首先,也是用MFC AppWizard[exe]生成一個dialog base的框架,在上面放一個Tree控件,並創建一個成員函數m_Tree來指向這個控件。
之後,編寫遞歸搜索函數
void AddFile( CString StrPath, HTREEITEM faItem );
最後,在OnInitDialog()中調用AddFile,這樣在程序已開始運行就顯示Tree。
詳細的內容請參考附帶的源代碼。
下面,我將着重講講如何編寫AddFile函數。對於文件目錄結構的遍歷,選用遞歸算法是不錯的。用遞歸方法對樹進行遍歷常用的有深度優先和廣度優先兩種搜索方法,由於我們要遍力整個樹,所以選用深度優先的算法是不錯的。因爲在這裏,深度優先和廣度優先的時間複雜度一樣,但是深度優先比較節省堆棧資源。以下,就是採用深度優先搜索的AddFile函數,我將在程序中作進一步說明。
// 遞歸搜索文件路徑,採用深度優先搜索法
void CFileTreeDlg::AddFile(CString StrPath, HTREEITEM faItem ) //StrPath爲傳遞過來的目錄層次,本次函數調用中搜索的文件都是它的下一層的。 //faItem爲傳遞過來的Tree節點,本次函數調用中添加的Tree節點都是它的子節點。 { CFileFind OneFile; CString FName, DirName; BOOL BeWorking; HTREEITEM NewItem; DirName = StrPath+"//*.*"; BeWorking = OneFile.FindFile( DirName ); while ( BeWorking ) { //BeWorking非零,指找了文件或目錄 //查找同級的目錄 BeWorking = OneFile.FindNextFile(); if ( OneFile.IsDirectory() && !OneFile.IsDots() ) //如果查找的結果是目錄又不是".."或"." { //向Tree1中添加目錄; DirName = OneFile.GetFilePath(); FName = OneFile.GetFileTitle(); //IDC_TREE1 NewItem = m_Tree.InsertItem( FName, faItem ); //NewItem取得節點,其目的是爲了下一層中 //添加節點方便,遞歸時把它傳過去。 //進入下一層遞歸調用。 AddFile(DirName, NewItem); } //退出遞歸時,到了這裏!!! if ( !OneFile.IsDirectory() && !OneFile.IsDots() ) //如果查找結果是文件 { //向Tree1中添加文件 FName = OneFile.GetFileTitle(); //注意這裏用的是GetFileTitle,因爲 //這裏是添加文件。 m_Tree.InsertItem( FName, faItem ); } }// end of while OneFile.Close(); //記着用完CFileFild實例要關閉 }
************************************
主要用到的類有:
CListCtrl,CTreeCtrl,CImageList,CFileFind 和函數SHGetFileInfo()
簡述步驟如下:
1、增加 TreeCtrl 的 TVS_HASBUTTONS,TVS_HASLINES、TVS_LINESATROOT Style,代碼如下:
DWORD dwStyle = GetWindowLong(m_tree.m_hWnd,GWL_STYLE); dwStyle |= TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT; SetWindowLong(m_tree.m_hWnd,GWL_STYLE,dwStyle);
2、爲TreeCtrl添加Root項:
m_hRoot = m_tree.InsertItem("我的電腦"); InsertItem()的函數原形爲 HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST );
3、獲取本地邏輯驅動器,並添加:
void CTreeViewDlg::GetLogicalDrives(HTREEITEM hParent) { size_t szAllDriveStrings = GetLogicalDriveStrings(0,NULL); char *pDriveStrings = new char[szAllDriveStrings + sizeof(_T(""))]; GetLogicalDriveStrings(szAllDriveStrings,pDriveStrings); size_t szDriveString = strlen(pDriveStrings); while(szDriveString > 0) { m_tree.InsertItem(pDriveStrings,hParent); pDriveStrings += szDriveString + 1; szDriveString = strlen(pDriveStrings); } }
4、添加TVN_EXPANDED消息處理函數,當一項展開時,爲其子項添加下一級目錄:
void CTreeViewDlg::OnItemexpandedTree(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // TODO: Add your control notification handler code here TVITEM item = pNMTreeView->itemNew; if(item.hItem == m_hRoot) return; HTREEITEM hChild = m_tree.GetChildItem(item.hItem); while(hChild) { AddSubDir(hChild); hChild = m_tree.GetNextItem(hChild,TVGN_NEXT); } *pResult = 0; }
AddSubDir函數功能添加子項,具體代碼見示例。
5、添加TVN_SELCHANGED消息處理函數,在這個函數裏,用GetFullPath()取得選中項的絕 路徑(GetFullPath()具體代碼看示例),在ListCtrl中添加文件而非文件夾的圖標:
void CTreeViewDlg::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult) { m_list.DeleteAllItems(); NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; TVITEM item = pNMTreeView->itemNew; if(item.hItem == m_hRoot) return; CString str = GetFullPath(item.hItem); if(str.Right(1) != "//") str += "//"; str += "*.*"; CFileFind file; BOOL bContinue = file.FindFile(str); while(bContinue) { bContinue = file.FindNextFile(); if(!file.IsDirectory() && !file.IsDots()) { SHFILEINFO info; CString temp = str; int index = temp.Find("*.*"); temp.Delete(index,3); SHGetFileInfo(temp + file.GetFileName(), 0, &info,sizeof(&info), SHGFI_DISPLAYNAME | SHGFI_ICON); int i = m_ImageList.Add(info.hIcon); m_list.InsertItem(i,info.szDisplayName,i); } } *pResult = 0; }
這只是一個簡單的例子,你可以在 ListCtrl 中添加鼠標雙擊消息的處理函數,用 Process 打開該選中的文件; 該示例在VC6,xp下編譯通過。
VC初學者,如有不足之處,請來信指教([email protected])。