效果图:
头文件定义(CSWDirectoryListCtrl.h):
#pragma once
#include "afxwin.h"
#include <afxcmn.h>
#include <string>
namespace sw {
class CSWDirectoryListCtrl: public CListCtrl
{
public:
CSWDirectoryListCtrl();
virtual ~CSWDirectoryListCtrl();
private:
CImageList* m_pImageList;
CImageList* m_pImageListL;
CMap<CString, LPCTSTR, INT, INT&> m_mapIcons;
UINT m_nCurFolderLevel;
CString m_strCurFolder;
CRect m_rcOrgClient;
BOOL m_bSortByAsc;
CString m_strExtFilter;
CString m_strCurFileName;
protected:
// 获取图标索引
INT GetFileIcon(CString strFileName);
// 获取文件类型
CString GetFileType(CString strFileName);
// 添加目录
UINT AddFolder(CString strFolder);
// 添加文件
UINT AddFile(CString strFile);
// 添加驱动器
UINT AddDriver(CString strDriver);
// 返回上一级目录
void BackParentFolder(CString strFolder);
// 扩展当前目录
BOOL ExpandCurFolder(CString strFolder);
// 设置默认根目录
void ResetDefalultFolder();
// 加载默认图标(常用)
void LoadDefaultIcon();
public:
// 升序排列
void SortByAsc();
// 降序排列
void SortByDesc();
// 设置文件后缀名过滤
void SetFilter(CString strFilter);
// 获取当前选择文件名
CString GetFileName();
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual void PreSubclassWindow();
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg BOOL OnNMDblclk(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
afx_msg void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
};
}
源码实现(CSWDirectoryListCtrl.cpp):
#include "pch.h"
#include "CSWDirectoryListCtrl.h"
using namespace sw;
CSWDirectoryListCtrl::CSWDirectoryListCtrl()
{
m_pImageList = NULL;
m_pImageListL = NULL;
m_bSortByAsc = TRUE;
m_nCurFolderLevel = 0;
m_strCurFolder = _T("");
m_strExtFilter = _T("");
m_rcOrgClient.SetRectEmpty();
}
CSWDirectoryListCtrl::~CSWDirectoryListCtrl()
{
}
BOOL sw::CSWDirectoryListCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此添加专用代码和/或调用基类
cs.style |= LVS_REPORT;
return __super::PreCreateWindow(cs);
}
BEGIN_MESSAGE_MAP(sw::CSWDirectoryListCtrl, CListCtrl)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_SIZE()
ON_NOTIFY_REFLECT_EX(NM_DBLCLK, &CSWDirectoryListCtrl::OnNMDblclk)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CSWDirectoryListCtrl::OnNMCustomdraw)
ON_WM_MEASUREITEM()
ON_WM_MEASUREITEM_REFLECT()
END_MESSAGE_MAP()
int sw::CSWDirectoryListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (__super::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
return 0;
}
CString CSWDirectoryListCtrl::GetFileType(CString strFileName)
{
int nFoundAt = strFileName.Find(".", 0);
if (nFoundAt == 0)
return strFileName + " 文件";
strFileName.MakeUpper();
CString strExt = strFileName.Mid(nFoundAt);
CString strResult = "未知";
if (strExt.GetLength() > 0)
{
SHFILEINFOA info;
if (SHGetFileInfo(strExt,
FILE_ATTRIBUTE_NORMAL,
&info,
sizeof(info),
SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES))
{
strResult = info.szTypeName;
}
}
return strResult;
}
INT CSWDirectoryListCtrl::GetFileIcon(CString strFileName)
{
DWORD dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
CString strExt = strFileName;
struct _stat buf;
int result = _stat(strFileName, &buf);
if (_S_IFDIR & buf.st_mode) {
dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
if (strFileName.GetLength() > 3)
strExt = _T("floder");
}
else if (_S_IFREG & buf.st_mode) {
int nFoundAt = strFileName.Find(".", 0);
strFileName.MakeUpper();
strExt = strFileName.Mid(nFoundAt);
}
INT nIndex = -1;
if (m_mapIcons.Lookup(strExt, nIndex))
return nIndex;
HICON hIcon = NULL;
if (strExt.GetLength() > 0)
{
SHFILEINFOA info;
if (SHGetFileInfo(strExt,
dwFileAttributes,
&info,
sizeof(info),
SHGFI_SYSICONINDEX | SHGFI_ICON | SHGFI_USEFILEATTRIBUTES))
{
hIcon = info.hIcon;
}
}
if (hIcon)
{
nIndex = m_pImageList->Add(hIcon);
nIndex = m_pImageListL->Add(hIcon);
m_mapIcons[strExt] = nIndex;
}
return nIndex;
}
void sw::CSWDirectoryListCtrl::OnDestroy()
{
__super::OnDestroy();
// TODO: 在此处添加消息处理程序代码
if (m_pImageList != NULL)
{
delete m_pImageList;
m_pImageList = NULL;
}
if (m_pImageListL != NULL)
{
delete m_pImageListL;
m_pImageListL = NULL;
}
}
void sw::CSWDirectoryListCtrl::OnSize(UINT nType, int cx, int cy)
{
__super::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
CRect rcClient; GetWindowRect(rcClient);
rcClient.OffsetRect(-rcClient.left, -rcClient.top);
if (GetSafeHwnd() && !rcClient.EqualRect(m_rcOrgClient))
{
m_rcOrgClient = rcClient;
SetColumnWidth(0, cx-4);
SetColumnWidth(1, 1);
SetColumnWidth(2, 1);
SetColumnWidth(3, 1);
SetColumnWidth(4, 1);
InvalidateRect(NULL);
}
}
UINT sw::CSWDirectoryListCtrl::AddFolder(CString strFolder)
{
CString strNewFolder = strFolder;
int nPos = strNewFolder.ReverseFind('\\');
if (nPos)
{
strNewFolder = strNewFolder.Right(strNewFolder.GetLength() - nPos - 1);
}
UINT nItem = GetItemCount();
nItem = InsertItem(nItem, strNewFolder, GetFileIcon(strFolder));
CFileFind ff;
if (ff.FindFile(strFolder))
{
ff.FindNextFile();
CTime tm;;
if (ff.GetLastWriteTime(tm))
{
CString strTime; strTime.Format("%d/%d/%d %d:%d", tm.GetYear(), tm.GetMonth(), tm.GetDay(), tm.GetHour(), tm.GetMinute());
SetItemText(nItem, 1, strTime);
}
}
SetItemText(nItem, 2, _T("文件夹"));
SetItemText(nItem, 3, _T(""));
SetItemText(nItem, 4, strFolder);
return nItem;
}
UINT sw::CSWDirectoryListCtrl::AddFile(CString strFile)
{
CString strNewFile = strFile;
int nPos = strNewFile.ReverseFind('\\');
if (nPos)
{
strNewFile = strNewFile.Right(strNewFile.GetLength() - nPos - 1);
}
UINT nItem = GetItemCount();
nItem = InsertItem(nItem, strNewFile, GetFileIcon(strFile));
CFileFind ff;
if (ff.FindFile(strFile))
{
ff.FindNextFile();
CTime tm;;
if (ff.GetLastWriteTime(tm))
{
CString strTime; strTime.Format("%d/%d/%d %d:%d", tm.GetYear(), tm.GetMonth(), tm.GetDay(), tm.GetHour(), tm.GetMinute());
SetItemText(nItem, 1, strTime);
}
}
SetItemText(nItem, 2, GetFileType(strFile));
LONGLONG llSize = ff.GetLength();
if (llSize<=1024)
SetItemText(nItem, 3, _T("1 KB"));
else if (llSize > 1024)
{
llSize = llSize / 1024;
CString s; s.Format(_T("%ld"), llSize);
s.MakeReverse();
CString sFormat;
for (int i = 0; i < s.GetLength(); i++)
{
sFormat += s[i];
if (i % 3 == 2)
sFormat += _T(",");
}
sFormat.MakeReverse();
sFormat += " KB";
SetItemText(nItem, 3, sFormat);
}
SetItemText(nItem, 4, strFile);
return nItem;
}
UINT sw::CSWDirectoryListCtrl::AddDriver(CString strDriver)
{
UINT nType = GetDriveType(strDriver);
if (GetDriveType(strDriver) == DRIVE_CDROM)
return -1;
UINT nItem = GetItemCount();
nItem = InsertItem(nItem, strDriver, GetFileIcon(strDriver));
CFileFind ff;
if (ff.FindFile(strDriver))
{
ff.FindNextFile();
CTime tm;;
if (ff.GetLastWriteTime(tm))
{
CString strTime; strTime.Format("%d/%d/%d %d:%d", tm.GetYear(), tm.GetMonth(), tm.GetDay(), tm.GetHour(), tm.GetMinute());
SetItemText(nItem, 1, strTime);
}
}
if (nType == DRIVE_REMOVABLE)
SetItemText(nItem, 2, _T("可移动磁盘"));
else if (nType == DRIVE_REMOTE)
SetItemText(nItem, 2, _T("网络磁盘"));
else
SetItemText(nItem, 2, _T("本地磁盘"));
SetItemText(nItem, 3, _T(""));
SetItemText(nItem, 4, strDriver);
return nItem;
}
struct ICON_DEFAULT
{
SHSTOCKICONID siid;
LPCSTR lpszName;
};
ICON_DEFAULT ICON_DEFAULT_ARRAY[]=
{
{SIID_FOLDERBACK, _T("返回上一级") },
{SIID_VIDEOFILES, _T("视频") },
{SIID_AUDIOFILES, _T("音乐") },
{SIID_DESKTOPPC, _T("桌面") },
{SIID_DRIVEREMOVE, _T("可移动磁盘") },
{SIID_DRIVENET, _T("网络磁盘") },
};
void sw::CSWDirectoryListCtrl::LoadDefaultIcon()
{
for (auto ao : ICON_DEFAULT_ARRAY)
{
SHSTOCKICONINFO sii = { 0 };
sii.cbSize = sizeof(SHSTOCKICONINFO);
if (SUCCEEDED(SHGetStockIconInfo(ao.siid, SHGFI_SYSICONINDEX | SHGSI_ICON | SHGSI_ICONLOCATION, &sii)))
{
if (sii.hIcon)
{
m_pImageList->Add(sii.hIcon);
UINT nIndex = m_pImageListL->Add(sii.hIcon);
m_mapIcons[ao.lpszName] = nIndex;
}
}
}
}
#include <shlobj_core.h>
// 绘制半透是,ILC_COLOR32只有在comctl32 6.0以上版本才支持.
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
void sw::CSWDirectoryListCtrl::PreSubclassWindow()
{
// TODO: 在此添加专用代码和/或调用基类
m_rcOrgClient.SetRectEmpty();
CRect rect;
GetClientRect(&rect);
InsertColumn(0, "名称", LVCFMT_LEFT, rect.right * 2 / 5, -1);
InsertColumn(1, "修改日期", LVCFMT_LEFT, rect.right / 5, -1);
InsertColumn(2, "类型", LVCFMT_LEFT, rect.right / 5, -1);
InsertColumn(3, "大小", LVCFMT_LEFT, rect.right / 5, -1);
InsertColumn(4, "名称", LVCFMT_LEFT, 0, -1);
m_pImageList = new CImageList();
m_pImageListL = new CImageList();
m_pImageList->Create(20, 20, ILC_COLOR32 | ILC_MASK, 9, 9);
m_pImageListL->Create(32, 32, ILC_COLOR32 | ILC_MASK, 9, 9);
SetImageList(m_pImageList, LVSIL_SMALL);
SetImageList(m_pImageListL, LVSIL_NORMAL);
LoadDefaultIcon();
ResetDefalultFolder();
__super::PreSubclassWindow();
}
void sw::CSWDirectoryListCtrl::BackParentFolder(CString strFolder)
{
if (m_nCurFolderLevel == 1)
ResetDefalultFolder();
else
{
CString strNewFolder = strFolder;
int nPos = strNewFolder.ReverseFind('\\');
if (nPos)
{
strNewFolder = strNewFolder.Left(nPos);
if (ExpandCurFolder(strNewFolder))
m_nCurFolderLevel--;
}
}
}
#include <vector>
#include <string>
#include <algorithm>
BOOL sw::CSWDirectoryListCtrl::ExpandCurFolder(CString strFolder)
{
if (strFolder.IsEmpty())
return FALSE;
SetRedraw(FALSE);
//LockWindowUpdate();
CFileFind ff;
BOOL bRet = ff.FindFile(strFolder + _T("\\*.*"));
std::vector<std::string> vectFolder;
std::vector<std::string> vectFile;
while (bRet)
{
bRet = ff.FindNextFile();
CString strFileName = ff.GetFileName();
TRACE("FileName=%s\n", strFileName);
TRACE("IsDots=%s\n", ff.IsDots() ? "TRUE" : "FALSE");
TRACE("IsReadOnly=%s\n", ff.IsReadOnly() ? "TRUE" : "FALSE");
TRACE("IsDirectory=%s\n", ff.IsDirectory() ? "TRUE" : "FALSE");
TRACE("IsCompressed=%s\n", ff.IsCompressed() ? "TRUE" : "FALSE");
TRACE("IsSystem=%s\n", ff.IsSystem() ? "TRUE" : "FALSE");
TRACE("IsHidden=%s\n", ff.IsHidden() ? "TRUE" : "FALSE");
TRACE("IsTemporary=%s\n", ff.IsTemporary() ? "TRUE" : "FALSE");
TRACE("IsNormal=%s\n", ff.IsNormal() ? "TRUE" : "FALSE");
TRACE("IsArchived=%s\n", ff.IsArchived() ? "TRUE" : "FALSE");
if (!ff.IsDots())
{
if (ff.IsDirectory() && !ff.IsHidden() && !ff.IsSystem())
{
vectFolder.push_back(ff.GetFilePath().GetBuffer(0));
}
else if (!ff.IsHidden() && !ff.IsSystem())
{
CString strExt = strFileName.Mid(strFileName.Find(".", 0));
TCHAR* pchEnd = m_strExtFilter.GetBuffer(0);
pchEnd = strstr(pchEnd, strExt.GetBuffer(0));
if (pchEnd)
vectFile.push_back(ff.GetFilePath().GetBuffer(0));
}
}
}
ff.Close();
auto SortByAsc = [](const std::string& file1, const std::string& file2)
{
bool bRet = false;
if (_stricmp(file1.c_str(), file2.c_str()) < 0)
bRet = true;
return bRet;
};
auto SortByDesc = [](const std::string& file1, const std::string& file2)
{
bool bRet = false;
if (_stricmp(file1.c_str(), file2.c_str()) < 0)
bRet = true;
return !bRet;
};
if (m_bSortByAsc)
{
std::sort(vectFolder.begin(), vectFolder.end(), SortByAsc);
std::sort(vectFile.begin(), vectFile.end(), SortByAsc);
}
else
{
std::sort(vectFolder.begin(), vectFolder.end(), SortByDesc);
std::sort(vectFile.begin(), vectFile.end(), SortByDesc);
}
DeleteAllItems();
HICON hIcon = NULL;
UINT nIndex = -1;
SHSTOCKICONINFO sii = { 0 };
sii.cbSize = sizeof(SHSTOCKICONINFO);
if (SUCCEEDED(SHGetStockIconInfo(SIID_FOLDERBACK, SHGFI_SYSICONINDEX | SHGSI_ICON | SHGSI_ICONLOCATION, &sii)))
{
hIcon = sii.hIcon;
if (hIcon)
{
nIndex = m_pImageList->Add(hIcon);
nIndex = m_pImageListL->Add(hIcon);
m_mapIcons[_T("返回上一级")] = nIndex;
}
}
UINT nItem = InsertItem(0, _T("返回上一级"), nIndex);
SetItemText(nItem, 4, _T("返回上一级"));
for (auto iter : vectFolder)
{
AddFolder(iter.c_str());
}
for (auto iter : vectFile)
{
AddFolder(iter.c_str());
}
//UnlockWindowUpdate();
SetRedraw(TRUE);
m_strCurFolder = strFolder;
if (vectFolder.size() > 0 || vectFile.size() > 0)
return TRUE;
else
return FALSE;
}
void sw::CSWDirectoryListCtrl::SortByAsc()
{
m_bSortByAsc = TRUE;
ExpandCurFolder(m_strCurFolder);
}
void sw::CSWDirectoryListCtrl::SortByDesc()
{
m_bSortByAsc = FALSE;
ExpandCurFolder(m_strCurFolder);
}
void sw::CSWDirectoryListCtrl::ResetDefalultFolder()
{
SetRedraw(FALSE);
DeleteAllItems();
auto AddDefaultFolder = [](CListCtrl* pListCtrl, const _In_ int iconindex, const _In_ int csidl, const _In_ std::string name)
{
char szFolder[_MAX_PATH] = { 0 };
if SUCCEEDED(SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, szFolder))
{
UINT nItem = pListCtrl->GetItemCount();
nItem = pListCtrl->InsertItem(nItem, name.c_str(), iconindex);
CFileFind ff;
if (ff.FindFile(szFolder))
{
ff.FindNextFile();
CTime tm;;
if (ff.GetLastWriteTime(tm))
{
CString strTime; strTime.Format("%d/%d/%d %d:%d", tm.GetYear(), tm.GetMonth(), tm.GetDay(), tm.GetHour(), tm.GetMinute());
pListCtrl->SetItemText(nItem, 1, strTime);
}
}
pListCtrl->SetItemText(nItem, 2, _T("文件夹"));
pListCtrl->SetItemText(nItem, 3, _T(""));
pListCtrl->SetItemText(nItem, 4, szFolder);
}
};
AddDefaultFolder(this, GetFileIcon(_T("视频")), CSIDL_MYVIDEO, _T("视频"));
AddDefaultFolder(this, GetFileIcon(_T("音乐")), CSIDL_MYMUSIC, _T("音乐"));
AddDefaultFolder(this, GetFileIcon(_T("桌面")), CSIDL_DESKTOP, _T("桌面"));
//Downloads
DWORD dwLen = GetLogicalDriveStrings(0, NULL);
TCHAR* pszDriver = new TCHAR[dwLen];
GetLogicalDriveStrings(dwLen, pszDriver);
TCHAR* pszEnd = pszDriver;
while (*pszEnd != '\0') {
AddDriver(pszEnd);
pszEnd += strlen(pszEnd) + 1;
}
delete[] pszDriver;
m_nCurFolderLevel = 0;
SetRedraw(TRUE);
}
BOOL sw::CSWDirectoryListCtrl::OnNMDblclk(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
if (pNMItemActivate->iItem >= 0)
{
m_strCurFileName.Empty();
CString strName = GetItemText(pNMItemActivate->iItem, 4);
struct _stat buf;
int result = _stat(strName, &buf);
if (_S_IFDIR & buf.st_mode) {
if (ExpandCurFolder(strName))
m_nCurFolderLevel++;
}
else if (_S_IFREG & buf.st_mode) {
m_strCurFileName = strName;
return FALSE;
}
else if (strName == _T("返回上一级"))
{
BackParentFolder(m_strCurFolder);
}
}
return TRUE;
}
void sw::CSWDirectoryListCtrl::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0; // Request item-specific notifications if this is the // beginning of the paint cycle.
switch (pNMCD->nmcd.dwDrawStage)
{
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
case CDDS_ITEMPREPAINT:
{
// This is the beginning of an item 's paint cycle.
LVITEM lvItem;
int nItem = static_cast <int> (pNMCD->nmcd.dwItemSpec);
int nSubItem = pNMCD->iSubItem;
BOOL bHasFocus = (this->GetSafeHwnd() == ::GetFocus());
ZeroMemory(&lvItem, sizeof(LVITEM));
lvItem.mask = LVIF_IMAGE | LVIF_STATE;
lvItem.iItem = nItem;
lvItem.iSubItem = nSubItem;
lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
this->GetItem(&lvItem);
if (lvItem.state & LVIS_SELECTED) {
if (bHasFocus)
{
pNMCD->clrTextBk = GetBkColor();// GetSysColor(COLOR_HIGHLIGHT);
pNMCD->clrText = GetTextColor()/*GetSysColor(COLOR_HIGHLIGHTTEXT)*/;
}
else {
pNMCD->clrTextBk = RGB(43, 43, 43);// GetSysColor(COLOR_BTNFACE);
pNMCD->clrText = GetTextColor()/*GetSysColor(COLOR_BTNTEXT)*/;
}
}
else {
pNMCD->clrTextBk = GetBkColor();// GetSysColor(COLOR_WINDOW);
pNMCD->clrText = RGB(171, 171, 171)/*GetSysColor(COLOR_BTNTEXT)*/;
}
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
}
default:
{
*pResult |= CDRF_NOTIFYPOSTPAINT;
*pResult |= CDRF_NOTIFYITEMDRAW;
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
}
}
}
// OnMeasureItem
void sw::CSWDirectoryListCtrl::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// TODO: Add your message handler code here and/or call default
// 设置列宽显示行高度
lpMeasureItemStruct->itemHeight = 25;
CListCtrl::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
//////////////////////////////////////////////////////////////////////////
// MeasureItem
void sw::CSWDirectoryListCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// 设置列宽显示行高度
lpMeasureItemStruct->itemHeight = 25;
}
void sw::CSWDirectoryListCtrl::SetFilter(CString strFilter)
{
m_strExtFilter = strFilter;
ExpandCurFolder(m_strCurFolder);
}
CString sw::CSWDirectoryListCtrl::GetFileName()
{
return m_strCurFileName;
}
详细注释,后期再补上,请见谅!!!