MFC实现资源管理器

该博客围绕MFC项目展开,介绍了基于对话框开发的文件管理界面。阐述了界面样式和功能要求,如显示磁盘目录树和文件列表、实现文件类型筛选、右键菜单等。详细说明了TreeView、ListControl等具体功能的实现步骤,还提及运行和打包exe文件的方法,并给出项目文件链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

界面样式

功能与要求

具体实现

 1.TreeView

2.ListControl

3.文件类型筛选

4.右键菜单

5.文件重命名

6.运行和打包exe文件

项目文件


界面样式

 

功能与要求

左边为磁盘目录树,右边为文件列表,需要实现功能
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

Github:https://github.com/Completist29/FileSystem

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Completits_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值