一、成果展示
二、实现功能
2.1 显示树形目录结构
- 根节点是“我的电脑”
- “我的电脑”下有几个盘符(C、D、E等)就有几个子节点,递归显 示文件系统下的所有文件信息(分支可以是目录也可以是文件,叶 子节点都是文件)
2.2 文件操作
能够创建目录、创建文件、删除目录、删除文件、复制文件、粘贴文件
三、需求分析
首先要求我们能够获取电脑本地的所有盘符;
然后利用递归将本地的所有文件夹以及文件都获取到;
接着将获取到的所有内容以树形目录结构的形式在UI界面显示,并且任一文件夹中的内容(文件夹、文件)可以以列表的形式在UI界面显示出来;
最后在写出的可视化界面上要能够对文件夹或者文件进行创建、删除、复制、粘贴等操作,这些操作应以可视化的形式显示并且要足够简单,让用户操作使用起来足够方便。
总体要求做出一个功能类似Windows 10操作系统中的“此电脑”的可视化应用程序,故可以仿照“此电脑”的UI设计及相关功能的操作方法,如下图所示。
四、系统设计
4.1 数据结构和存储结构的设计
因为这个文件系统的程序需要实时读取当前磁盘空间的存储状态及存储内容并且在C++中具有读取文件或文件夹状态以及路径的API函数,所以在程序中对于文件或文件夹的操作并不需要其他什么特殊的数据结构和存储结构,只要在使用树控件的时候利用现成的API获取相应的信息并将这些信息在树形控件中显示出来即可。
若想存储文件或文件夹的信息,可以采用树结构来存储,一个结点的孩子结点是这个结点所代表的文件夹中的所有文件或者其他文件夹,其中文件一定处在叶子结点上;其次,为了查询的方便、快速以及IO开销尽可能小,可以采用B-树或者B+树的结构存储,可以减小IO开销,提高查询效率。
其他的一些数据结构,例如栈、堆、队列、数组等,用法、设计与其余所有C++程序相同。(比如调用函数,利用了堆栈等)
4.2 算法设计
4.2.1 获取与删除目录: 递归算法
4.2.1.1 获取所有盘符的下一级目录
// 获取目录下所有子项 void CFileViewSystemDlg::GetDriveDir(HTREEITEM hParent) { HTREEITEM hChild = m_tree.GetChildItem(hParent); while (hChild) { CString strText = m_tree.GetItemText(hChild); // 检索列表中项目文字 if (strText.Right(1) != "\\") // 从右边1开始获取从右向左nCount个字符 strText += _T("\\"); strText += "*.*"; // 将当前目录下文件枚举并InsertItem树状显示 CFileFind file; // 定义本地文件查找 BOOL bContinue = file.FindFile(strText); // 查找包含字符串的文件 while (bContinue) { bContinue = file.FindNextFile(); // 查找下一个文件 if (file.IsDirectory() && !file.IsDots()) // 找到文件为内容且不为点"." m_tree.InsertItem(file.GetFileName(), hChild); // 添加盘符路径下树状文件夹 } GetDriveDir(hChild); // 递归调用 hChild = m_tree.GetNextItem(hChild, TVGN_NEXT); // 获取树形控件TVGN_NEXT下兄弟项 } }
4.2.1.2 删除目录及目录中的所有内容
需要删除一个目录首先要删除该目录下的所有文件和文件夹,然后再移除空文件夹,同样删除该文件夹下的文件夹也需要先删除其中的文件和文件夹,就形成了递归。
// 删除文件夹 bool CFileViewSystemDlg::DeleteFolder(LPCTSTR pstrFolder) { if ((NULL == pstrFolder)) { return FALSE; } // 检查输入目录是否是合法目录 if (!IsDirectory(pstrFolder)) { return FALSE; } // 创建源目录中查找文件的通配符 CString strWildcard(pstrFolder); if (strWildcard.Right(1) != _T('\\')) { strWildcard += _T("\\"); } strWildcard += _T("*.*"); // 打开文件查找,查看源目录中是否存在匹配的文件 // 调用FindFile后,必须调用FindNextFile才能获得查找文件的信息 CFileFind finder; BOOL bWorking = finder.FindFile(strWildcard); while (bWorking) { // 查找下一个文件 bWorking = finder.FindNextFile(); // 跳过当前目录“.”和上一级目录“..” if (finder.IsDots()) { continue; } // 得到当前目录的子文件的路径 CString strSubFile = finder.GetFilePath(); // 判断当前文件是否是目录, // 如果是目录,递归调用删除目录, // 否则,直接删除文件 if (finder.IsDirectory()) { if (!DeleteFolder(strSubFile)) { finder.Close(); return FALSE; } } else { if (!DeleteFile(strSubFile)) { finder.Close(); return FALSE; } } } // 关闭文件查找 finder.Close(); // 删除当前空目录 return RemoveDirectory(pstrFolder); }
4.2.2 判断文件是否存在:计数器累加法
每一次需要创建文件或者文件夹,都从 1 开始若文件或文件夹已存在计数器就加 1,形成新的文件夹或文件的名称,直到没有重复时就以该名称创建文件或文件夹。
文件夹:
str += _T("新建文件夹1"); while (PathIsDirectory(str)) { str.Delete(str.GetLength() - 1, 1); CString chg; chg.Format(_T("%d"), f_cnt); str += chg; f_cnt++; }
文本文档:
str += _T("新建文本文档1.txt"); while (PathFileExists(str)) { CString chg; chg.Format(_T("%d"), t_cnt); str.SetAt(str.Find(_T(".")) - 1, chg.GetAt(0)); t_cnt++; } CFile file(str, CFile::modeCreate); file.Close();
DOCX 文档:
str += _T("新建DOCX文档1.docx"); while (PathFileExists(str)) { CString chg; chg.Format(_T("%d"), d_cnt); str.SetAt(str.Find(_T(".")) - 1, chg.GetAt(0)); d_cnt++; } CFile file(str, CFile::modeCreate); file.Close();
4.2.3 读取一个文件夹下的所有文件或文件夹:文件搜索算法
运用 MFC 中的 CFileFind 类中的 FindFile(str)获取一个文件夹下面的所有文件和文件夹。
CFileFind file; BOOL bContinue = file.FindFile(str); while (bContinue) { bContinue = file.FindNextFileW(); if (!file.IsDots()) { //文件和文件夹 SHFILEINFO info = { 0 }; CString temp = str; int index = temp.Find(_T("*.*")); 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.SetImageList(&m_ImageList, LVSIL_SMALL); //设置图标 m_list.InsertItem(i, info.szDisplayName, i); //在列表插入一项 } }
4.3 模块设计
4.3.1 树形控件显示模块
1、获取所有盘符并在树形控件中的根节点“我的电脑”的孩子结点上插入:
void FileViewSystemDlg::GetLogicalDrive(HTREEITEM hParent)
2、获取所有盘符下的所有文件夹并在相应盘符结点的孩子结点上插入(仅仅是盘符的下一级):
void FileViewSystemDlg::GetDriveDir(HTREEITEM hParent)
注:盘符的下一级单独处理的原因是盘符与普通文件夹不同需要单独处理。
3、返回某一结点的绝对路径(从结点开始向根节点回溯):
CString FileViewSystemDlg::GetFullPath(HTREEITEM hCurrent)
4、获取某一文件夹下的所有文件夹并在代表该文件夹的结点的孩子结点上插入子文件
夹:
void FileViewSystemDlg::AddSubDir(HTREEITEM hParent)
5、展开树形控件某一结点时触发的事件(显示该节点下的所有文件夹并载入再下一层的文件夹):
void FileViewSystemDlg::OnItemexpandedTree(NMHDR* pNMHDR, LRESULT* pResult)
4.3.2 列表控件显示模块
1、选择树形控件的某一节点时触发的事件(将该结点代表的文件下的所有文件夹以及文件显示在列表控件中):
void FileViewSystemDlg::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult)
2、鼠标左键双击列表控件时触发的事件(文件夹:打开该文件夹在列表控件中显示;文件:调用外部应用程序打开文件)(未选中任何一项无动作):
void FileViewSystemDlg::OnDblclkList(NMHDR* pNMHDR, LRESULT* pResult)
3、鼠标右键单击列表控件时触发的事件(选中了某一项时显示操作菜单:打开、复制、删除;未选中某一项时显示操作菜单:刷新、新建、粘贴):
void FileViewSystemDlg::OnRclickList(NMHDR* pNMHDR, LRESULT* pResult)
4.3.3 返回、转到与显示地址模块
1、单击“返回”按钮时触发的事件(返回上一级目录):
void FileViewSystemDlg::OnClickedBack()
2、单击“转到”按钮时触发的事件(进入下拉列表框中输入的路径所表示的文件夹内):
void FileViewSystemDlg::OnClickedEnter()
3、下拉列表框中所选项发生变化时所触发的事件(进入下拉列表框所选项表示的文件夹内):
void FileViewSystemDlg::OnSelchangeDirpath()
4.3.4 创建模块
注:文件、文件夹的命名均通过计数器来避免名称冲突而创建失败。
1、鼠标右键单击列表空白处后弹出菜单,再鼠标左键单击菜单的“新建-文件夹”时触发的事件(在操作的目录下新建一个文件夹):
void FileViewSystemDlg::OnNewfile()
2、鼠标右键单击列表空白处后弹出菜单,再鼠标左键单击菜单的“新建-文本文档”时触发的事件(在操作的目录下新建一个文本文档):
void FileViewSystemDlg::OnTxt()
3、鼠标右键单击列表空白处后弹出菜单,再鼠标左键单击菜单的“新建-DOCX 文档”时触发的事件(在操作的目录下新建一个 DOCX 文档):
void FileViewSystemDlg::OnDocx()
4.3.5 删除模块
1、判断一个路径是否是已存在的文件夹
booFileViewSystemDlg::IsDirectory(LPCTSTR pstrPath)
2、删除文件夹及文件夹中的所有内容
booFileViewSystemDlg::DeleteFolder(LPCTSTR pstrFolder)
3、选中列表中的某一项后鼠标右键单击后弹出菜单,再鼠标左键单击菜单的“删除”时触发的事件(删除选中的文件夹或者文件):
void FileViewSystemDlg::OnDelete()
注:如果删除的是文件可以直接调用 mfc 的函数来删;如果删除的是文件夹,那么就需要先删除该文件夹下所有的文件夹以及文件后再移除该空文件夹(递归思想)。
4.3.6 复制模块
1、将参数(要复制的文件或文件夹的绝对路径)赋给成员变量 dir_path:
void FileViewSystemDlg::CopyToClipboard(CString dirPath)
2、选中列表中的某一项后鼠标右键单击后弹出菜单,再鼠标左键单击菜单的“复制”时触发的事件(得到选中项的绝对路径赋给成员变量 dir_path)(在事件处理中调用
CopyToClipboard(dirPath)函数: void FileViewSystemDlg::OnCopy()
注:将要复制的文件或文件夹的绝对路径赋给成员变量 dir_path 是为了在粘贴时调用
CopyFile(dir_path, dirPath, TRUE)函数。
4.3.7 粘贴模块
1、将经过上述复制操作的文件或者文件夹粘贴到参数 dirPath 目录下:
void FileViewSystemDlg::PasteToFile(CString dirPath)
2、鼠标右键单击列表空白处后弹出菜单,再鼠标左键单击菜单的“粘贴”时触发的事件(在操作的目录下粘贴已复制的文件或者文件夹):
void FileViewSystemDlg::OnPaste()
注:粘贴文件夹时是先创建一个文件夹,然后再在这个新创建的文件夹里面粘贴文件,嵌套(递归)。
4.4 类的函数成员和数据成员设计
注:在程序中主要是在 FileViewSystemDlg 类进行编程,所以另外两个自动生成的类这里不做赘述,下面是 FileViewSystemDlg 类的类定义,其中用到的数据成员都给了注释,而用到的成员函数在上一节“模块设计”小节以及本章最后一小节“其他模块设计与实现”小节中已经说明。
4.4.1 FileViewSystemDlg 类的定义
// FileViewSystemDlg.h: 头文件 // #pragma once // CFileViewSystemDlg 对话框 class CFileViewSystemDlg : public CDialogEx { // 构造 public: CFileViewSystemDlg(CWnd* pParent = nullptr); // 标准构造函数 // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_FILEVIEWSYSTEM_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: // 窗体控件变量 CComboBox m_combo; CListCtrl m_list; CTreeCtrl m_tree; protected: HTREEITEM m_hRoot; // HTREEITEM其实就是CTreeCtrl控件的项句柄,也就是一个DWORD值 CImageList m_ImageList; // CString dir_path; // 存当前展开目录字符串 int f_cnt; int t_cnt; int d_cnt; public: // 窗体控件绑定事件 afx_msg void OnBnClickedBack(); afx_msg void OnBnClickedEnter(); afx_msg void OnCbnSelchangeDirpath(); afx_msg void OnTvnItemexpandedTree(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnTvnKeydownTree(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnNMRClickList(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult); // 右键菜单栏绑定事件 afx_msg void OnOpen(); afx_msg void OnCopy(); afx_msg void OnDelete(); afx_msg void OnNewfile(); afx_msg void OnTxt(); afx_msg void OnDocx(); afx_msg void OnRefresh(); afx_msg void OnPaste(); // 获取系统分区驱动器字符串信息 void GetLogicalDrive(HTREEITEM hParent); // 获取驱动地址 void GetDriveDir(HTREEITEM hParent); // 获取树项目全根路径 CString GetFullPath(HTREEITEM hCurrent); // 添加子目录 void AddSubDir(HTREEITEM hParent); // 刷新/更新ListCtrl void Refresh(CString str); // 删除文件夹 bool DeleteFolder(LPCTSTR pstrFolder); // 目录是否存在 bool IsDirectory(LPCTSTR pstrPath); // 复制到剪贴板 void CopyToClipboard(CString dirPath); // 粘贴文件 void PasteToFile(CString dirPath); };
五、开发环境
操作系统:Windows 10
编程语言:C++(MFC)
开发环境及编译器:Visual Studio 2017
六、项目链接
CSDN下载:https://download.csdn.net/download/weixin_45525272/75053470
GitHub地址: