目录
界面样式
功能与要求
左边为磁盘目录树,右边为文件列表,需要实现功能
1、对话框启动,左边树上要有本电脑的磁盘目录信息
2、鼠标点到,树上的某个节点,右边列表上显示出当前树节点对应目录下的所有文件(只显示当前级,不考虑子文件夹)
3、右侧列表上要有右键菜单,内容有大图标,小图标,详细信息,三种显示方式,选择不同的项,列表相应的以所选方式显示文件
4、对话框下边的三个单选按钮,为文件类型,可根据所选的文件类型,查询出当前所选树节点对应目录下的所有此类文件
5、编辑框为文件名,把文件列表中选中的文件(此外为单选,不考虑多选)的文件名改为编辑框中的文件
具体实现
创建MFC项目,基于对话框开发,项目名为FileSystem;
在pch.h中添加如下代码
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include<experimental/filesystem>
#include<afx.h>
#include<afxwin.h>
#include <ShlObj.h>
#endif //PCH_H
1.TreeView
首先在工具箱选择GroupBox,主要为TreeControl控件提供标题,修改GroupBox的描述文字为"磁盘目录"。拖动TreeControl放入GroupBox中,设置ID为IDC_TREE。属性修改如下图所示:
主要修改始终显示所选内容为true。
随后右键TreeControl,添加变量 m_tree,类型为控件
接着开始代码实现:
在FileSystemDlg.h ,class CFileSystemDlg中声明
//FileSystemDlg.h
public:
CTreeCtrl m_tree;
//当前文件夹路径
CString m_strPath;
// 获取系统图标遍历逻辑驱动器,插入到 TreeView 控件
void FillDiskTree();
//递归获取指定目录下的子目录
void FillDirectories(HTREEITEM hParentItem, const CString& strPath);
在FileSystemDlg.cpp 中定义:
void CFileSystemDlg::FillDiskTree()
{
m_tree.DeleteAllItems(); // 清空 TreeView
// 使用 Shell API 获取磁盘信息
SHFILEINFO shfi;
HIMAGELIST himlSysSmall = (HIMAGELIST)SHGetFileInfo(L"C:\\", 0, &shfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
// 设置图标列表
m_tree.SetImageList(CImageList::FromHandle(himlSysSmall), TVSIL_NORMAL);
// 获取逻辑驱动器的位掩码
DWORD dwDrives = GetLogicalDrives();
// 遍历每个驱动器并插入到 TreeView 中
for (int i = 0; i < 26; ++i) {
if (dwDrives & (1 << i)) {
CString strDrive;
strDrive.Format(_T("%c:\\"), _T('A') + i);
// 添加驱动器到 TreeView
HTREEITEM hDriveItem = m_tree.InsertItem(strDrive, shfi.iIcon, shfi.iIcon);
m_tree.SetItemData(hDriveItem, (DWORD_PTR)i); // 将驱动器索引作为数据存储
// 递归获取驱动器下的目录
FillDirectories(hDriveItem, strDrive);
}
}
}
void CFileSystemDlg::FillDirectories(HTREEITEM hParentItem, const CString& strPath)
{
//只显示一级文件夹,资源消耗量小
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile(strPath + _T("*"), &findFileData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
_tcscmp(findFileData.cFileName, _T(".")) != 0 &&
_tcscmp(findFileData.cFileName, _T("..")) != 0) {
CString* pPath = new CString(strPath + findFileData.cFileName + _T("\\"));
HTREEITEM hDirItem = m_tree.InsertItem(findFileData.cFileName, 1, 1, hParentItem);
m_tree.SetItemData(hDirItem, reinterpret_cast<DWORD_PTR>(pPath));
}
} while (FindNextFile(hFind, &findFileData) != 0);
FindClose(hFind);
}
}
同时在OnInitDialog中:
BOOL CFileSystemDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_tree.ModifyStyle(0, TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP);
m_tree.InsertItem(_T("Dummy")); // 添加一个虚拟根节点,以便能够接收 TVN_SELCHANGED 消息
m_tree.SelectItem(m_tree.GetRootItem()); // 选中虚拟根节点
FillDiskTree();
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
现在就实现了TreeControl的显示。
刚才的FillDirectories()函数只实现了一级文件夹显示,若要显示所有文件夹,运行事件和消耗资源都挺多的,接下来提供查找全部文件夹的实现:
void CFileSystemDlg::FillDirectories(HTREEITEM hParentItem, const CString& strPath)
{
//递归显示所有文件夹,资源消耗量大
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile((strPath + _T("*.*")).GetString(), &findFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
return;
}
do
{
// 忽略当前目录和上级目录
if (_tcscmp(findFileData.cFileName, _T(".")) == 0 || _tcscmp(findFileData.cFileName, _T("..")) == 0)
continue;
// 如果是子目录,则递归添加到 TreeView
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CString strDirName = findFileData.cFileName;
CString strFullPath = strPath + strDirName + _T("\\");
// 添加子目录节点
HTREEITEM hChildItem = m_tree.InsertItem(strDirName, hParentItem);
// 设置图标
m_tree.SetItemImage(hChildItem, 1, 1);
// 递归填充子目录
FillDirectories(hChildItem, strFullPath);
}
} while (FindNextFile(hFind, &findFileData) != 0);
FindClose(hFind);
}
2.ListControl
同理,在工具箱选择GroupBox,,修改GroupBox的描述文字为"文件列表"。拖动ListControl放入GroupBox中,设置ID为IDC_LIST。属性修改如下图所示:
右键添加变量 m_list,类型为控件
在FileSystemDlg.h ,class CFileSystemDlg中声明:
//右键添加变量时,自动生成
CListCtrl m_list;
//存储当前文件夹路径
CString m_strPath;
void OnDestroy();
void FillFileList(const CString& strPath);
CString GetItemPath(CTreeCtrl* pTreeCtrl, HTREEITEM hItem);
在FileSystemDlg.cpp中实现:
void CFileSystemDlg::OnDestroy()
{
// TODO: 在此处添加实现代码.
// 清理树控件的存储
HTREEITEM hItem = m_tree.GetRootItem();
while (hItem != NULL) {
CString* pPath = reinterpret_cast<CString*>(m_tree.GetItemData(hItem));
delete pPath;
hItem = m_tree.GetNextItem(hItem, TVGN_NEXT);
}
CDialogEx::OnDestroy();
}
void CFileSystemDlg::FillFileList(const CString& strPath)
{
// TODO: 在此处添加实现代码.
m_CurrentFolderPath = strPath;
//清空 List Control
m_list.DeleteAllItems();
m_list.InsertColumn(0, _T("文件名"), LVCFMT_CENTER, 120);
m_list.InsertColumn(1, _T("文件类型"), LVCFMT_CENTER, 100);
// 使用 FindFirstFile 和 FindNextFile 遍历文件
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile((strPath + _T("*.*")).GetString(), &findFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
// 忽略当前目录和上级目录
if (_tcscmp(findFileData.cFileName, _T(".")) == 0 || _tcscmp(findFileData.cFileName, _T("..")) == 0)
continue;
// 添加文件信息到 List Control
int nIndex = m_list.InsertItem(0, findFileData.cFileName);
m_list.SetItemText(nIndex, 1, (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _T("文件夹") : _T("文件"));
} while (FindNextFile(hFind, &findFileData) != 0);
FindClose(hFind);
}
}
CString CFileSystemDlg::GetItemPath(CTreeCtrl* pTreeCtrl, HTREEITEM hItem)
{
// TODO: 在此处添加实现代码.
CString strPath;
// 获取所选节点的文本
CString strItemText = pTreeCtrl->GetItemText(hItem);
// 递归获取节点的父节点
HTREEITEM hParentItem = pTreeCtrl->GetParentItem(hItem);
if (hParentItem != nullptr)
{
// 如果不是根节点,继续递归
strPath = GetItemPath(pTreeCtrl, hParentItem);
}
// 添加当前节点的文本到路径中
strPath += strItemText;
// 如果不是根节点,添加路径分隔符
if (hParentItem != nullptr)
{
strPath += _T("\\");
}
// 调试输出
//TRACE(_T("Selected Folder Path: %s\n"), strPath);
return strPath;
}
在OnInit中添加:
FillDiskTree();
m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
想要调用这些函数,就需要在事件中设置。
首先点击资源视图->Dialog->IDD_FILESYSTEM_DIALOG,点击其中的TreeControl,在其属性页中点击事件,随后在TVN_SELCHANGED添加事件。
完成后,在FileSystemDlg.h会自动生成声明:
afx_msg void OnTvnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult);
在FileSystemDlg.cpp的OnTvnSelchangedTree()定义中添加代码:
void CFileSystemDlg::OnTvnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
// 获取 Tree View 控件
CTreeCtrl* pTreeCtrl = &m_tree;
// 获取所选节点
HTREEITEM hSelectedItem = pTreeCtrl->GetSelectedItem();
if (hSelectedItem == nullptr)
return;
// 获取节点的完整路径
m_strPath = GetItemPath(pTreeCtrl, hSelectedItem);
TRACE(_T("m_strPath: %s\n"), m_strPath);
CString strSelectedPath = GetItemPath(pTreeCtrl, hSelectedItem);
// 填充 List Control
FillFileList(strSelectedPath);
*pResult = 0;
}
此时就实现了点击树状图中的节点,ListControl会显示当前一级的所有文件。
注意,程序中使用类似于:
TRACE(_T("Selected Folder Path: %s\n"), strPath);
这样的代码。目的是方便调试,确定在某一运行阶段,某个变量的值。在Debug环境下,程序运行时,会在输出界面显示这些变量的值:
这样就可以方便你找到出错的地方。
3.文件类型筛选
在IDD_FILESYSTEM_DIALOG界面,从工具箱中拖取三个RadioButton。将他们的描述文字分别改为:,doc .xlx .jpg ;ID为:IDC_DocFile_RADIO、IDC_XlsFile_RADIO、IDC_JpgFile_RADIO
然后在FileSystemDlg.h中,添加:
#include<vector>
enum FileType
{
FileType_Doc,
FileType_Xls,
FileType_Jpg,
FileType_Unknown
};
添加的位置如下图
同时分别对三个RadioButton进行添加变量,类型为值变量。
随后,双击你的RadioButton分别添加BN_CLICKEDE事件。
void CFileSystemDlg::OnBnClickedDocfileRadio()
{
// TODO: 在此添加控件通知处理程序代码
m_IsDocFileSelected = true;
m_IsXlsFileSelected = false;
m_IsJpgFileSelected = false;
}
void CFileSystemDlg::OnBnClickedXlsfileRadio()
{
// TODO: 在此添加控件通知处理程序代码
m_IsDocFileSelected = false;
m_IsXlsFileSelected = true;
m_IsJpgFileSelected = false;
}
void CFileSystemDlg::OnBnClickedJpgfileRadio()
{
// TODO: 在此添加控件通知处理程序代码
m_IsDocFileSelected = false;
m_IsXlsFileSelected = false;
m_IsJpgFileSelected = true;
}
在FileSystemDlg.h中声明:
public:
void QueryFiles(const CString& strPath);
bool IsDocFile(const CString& fileName);
bool IsXlsFile(const CString& fileName);
bool IsJpgFile(const CString& fileName);
定义:
// 在 QueryFiles 函数中执行文件查询,并更新 List Control
void CFileSystemDlg::QueryFiles(const CString& strPath)
{
// 清空 List Control
m_list.DeleteAllItems();
// 使用 FindFirstFile 和 FindNextFile 遍历文件
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile((strPath + _T("*.*")).GetString(), &findFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
// 忽略当前目录和上级目录
if (_tcscmp(findFileData.cFileName, _T(".")) == 0 || _tcscmp(findFileData.cFileName, _T("..")) == 0)
continue;
// 根据文件类型过滤文件
if ((m_IsDocFileSelected && IsDocFile(findFileData.cFileName)) ||
(m_IsXlsFileSelected && IsXlsFile(findFileData.cFileName)) ||
(m_IsJpgFileSelected && IsJpgFile(findFileData.cFileName)))
{
// 添加文件信息到 List Control
int nIndex = m_list.InsertItem(0, findFileData.cFileName);
m_list.SetItem(nIndex, 0, LVIF_IMAGE | LVIF_TEXT, findFileData.cFileName, 0, 0, 0, 0);
}
} while (FindNextFile(hFind, &findFileData) != 0);
FindClose(hFind);
}
}
//判断文件类型
bool CFileSystemDlg::IsDocFile(const CString& fileName)
{
return fileName.Right(4).CompareNoCase(_T(".doc")) == 0;
}
bool CFileSystemDlg::IsXlsFile(const CString& fileName)
{
return fileName.Right(4).CompareNoCase(_T(".xls")) == 0;
}
bool CFileSystemDlg::IsJpgFile(const CString& fileName)
{
return fileName.Right(4).CompareNoCase(_T(".jpg")) == 0;
}
添加一个Button,秒描述文字为“查询”,ID为IDC_FIND_BUTTON;双击该按钮,跳转到BN_CLICKEDE事件。
// 在 OnBnClickedFindButton 函数中处理查询按钮点击事件
void CFileSystemDlg::OnBnClickedFindButton()
{
// 获取当前选定的目录
HTREEITEM hSelectedItem = m_tree.GetSelectedItem();
CString strPath = GetItemPath(&m_tree, hSelectedItem);
// 查询当前目录下的指定文件类型
QueryFiles(strPath);
}
4.右键菜单
点击 资源管理视图-FileSystem-右键添加资源-Menu。随后进行编辑,格式如下:
在IDD_FILESYSTEM_DIALOG页面中,点击你的ListControl,在属性页添加NM_RCLICK事件:
代码如下:
void CFileSystemDlg::OnNMRClickList(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1)
{
CPoint pt; GetCursorPos(&pt);
CMenu menu; menu.LoadMenu(IDR_MENU1);
CMenu* popmenu; popmenu = menu.GetSubMenu(0);
popmenu->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
}
*pResult = 0;
}
分别右键点击菜单页的三个选项,添加事件处理程序
转到函数定义:
//右键菜单-大图标
void CFileSystemDlg::OnLargeIcon()
{
// TODO: 在此添加命令处理程序代码
m_list.ModifyStyle(LVS_TYPEMASK, LVS_ICON);
}
//右键菜单-小图标
void CFileSystemDlg::OnSmallIcon()
{
// TODO: 在此添加命令处理程序代码
//设置List Control的显示方式为小图标
m_list.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);
}
//右键菜单-详细信息
void CFileSystemDlg::OnDetails()
{
// TODO: 在此添加命令处理程序代码
//设置List Control的显示方式为详细信息
m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
}
5.文件重命名
添加一个EditControl和一个Button,ID分别为:IDC_FILENAME_EDIT、IDC_RENAME_BUTTON;
为EditControl添加变量:
在FileSystem.h中添加声明:
int m_SelectedFileIndex;
CString m_NewFileName;
CString m_CurrentFolderPath
转到EditControl的属性页,添加EN_CHANGE事件。
转到函数定义:
void CFileSystemDlg::OnEnChangeFilenameEdit()
{
// TODO: 如果该控件是 RICHEDIT 控件,它将不
// 发送此通知,除非重写 CDialogEx::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
GetDlgItemText(IDC_FILENAME_EDIT, m_NewFileName);
TRACE(_T("m_NewFileName 更新:%s\n"), m_NewFileName);
}
点击你的ListControl控件,跳转到属性页,添加LVN_ITEMCHANGED事件
转到函数定义:
void CFileSystemDlg::OnLvnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// 检查是否选中了新的项
if ((pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & LVIS_SELECTED))
{
m_SelectedFileIndex = pNMLV->iItem; // 更新选中文件的索引
CString selectedFileName = m_list.GetItemText(m_SelectedFileIndex, 0);
// 将选中文件的名称显示在 EditControl 中
SetDlgItemText(IDC_FILENAME_EDIT, selectedFileName);
// 将编辑框中的文本保存到变量中
GetDlgItemText(IDC_FILENAME_EDIT, m_NewFileName);
TRACE(_T("m_NewFileName:%s m_CurrentFolderPath:%s \n"), m_NewFileName,m_CurrentFolderPath);
}
else
{
TRACE(_T("EditControl Display error!\n"));
}
*pResult = 0;
}
双击 重命名Button,转到函数定义:
void CFileSystemDlg::OnBnClickedRenameButton()
{
// TODO: 在此添加控件通知处理程序代码
if (m_SelectedFileIndex != -1)
{
// 获取选中文件的旧名称
CString oldFileName = m_list.GetItemText(m_SelectedFileIndex, 0);
// 获取当前文件的完整路径
CString currentFilePath = m_CurrentFolderPath + oldFileName;
// 将 List Control 中选中文件的文本设置为新文件名
m_list.SetItemText(m_SelectedFileIndex, 0, m_NewFileName);
// 构建新文件的完整路径
CString newFilePath = m_CurrentFolderPath + m_NewFileName;
TRACE(_T("currentFilePath:%s newFilePath:%s \n"), currentFilePath, newFilePath);
if (MoveFile(currentFilePath, newFilePath))
{
// 更新 List Control 中的显示
m_list.SetItemText(m_SelectedFileIndex, 0, m_NewFileName);
}
else
{
MessageBox(_T("重命名失败"), _T("错误"), MB_OK | MB_ICONERROR);
}
}
else
{
TRACE(_T("No item selected in List Control.\n"));
}
}
在OnInitDialog()函数中:
BOOL CFileSystemDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化代码
m_tree.ModifyStyle(0, TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP);
m_tree.InsertItem(_T("Dummy")); // 添加一个虚拟根节点,以便能够接收 TVN_SELCHANGED 消息
m_tree.SelectItem(m_tree.GetRootItem()); // 选中虚拟根节点
FillDiskTree();
m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
// 初始化文件类型变量
m_IsDocFileSelected = false;
m_IsXlsFileSelected = false;
m_IsJpgFileSelected = false;
// 初始化为无效的索引
m_SelectedFileIndex = -1;
// 初始化当前路径
m_CurrentFolderPath = _T("C:\\");
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
就实现了重命名功能。
6.运行和打包exe文件
对你的项目工程右键,在属性页中设置。
首先在配置属性-高级中,将配置改为Release,MFC的使用改为在静态库中使用MFC
然后在配置属性->C/C++->代码生成->运行库中设置为多线程(/MT);
最后点击确定,退出。
如下图,将Debug改为Release
运行一次你的项目后,在解决方案管理器中右键点击你的项目名,选择在“文件资源管理器中打开文件夹”
点击x64->Release文件夹,就出现了FileSystem.exe
项目文件
CSDN:https://download.csdn.net/download/Completits_/88564900