在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初学者,如有不足之处,请来信指教(waysen01@st.lzu.edu.cn)。