我开发了一个像文件浏览器一样的应用程序
有listview
显示目录中的文件和子文件夹。
如何在列表视图中显示文件和文件夹图标?
以下方式,将文件路径添加到列表视图。我想展示图标:
string[] s = Directory.GetDirectories(file);
foreach (string file in s)
{
listView1.Items.Add(file);
}
答案 0 :(得分:9)
有几种不同的方法可以做到这一点。一种方法是创建一个ImageList并将其链接到ListView,然后检索每个文件的图标,将其添加到ImageList,并设置ListViewItem以在ImageList中的相应索引处显示图标。
另一种方法是利用由shell维护的系统映像列表。这样可以避免自己维护图标的重复副本。想象一下,如果ListView中有一堆文件夹。所有这些都将具有相同的图标,但没有一丝特别的关注,您将保存该文件夹图标的多个副本,因为您正在显示使用它的项目。 shell实现会为您处理所有这些重复跟踪。系统映像列表仅包含所需的图标(您明确请求的图标),然后仅包含每个图标的单个副本。我认为这是一个更清洁,更优雅的设计,所以让我们实现它。我们需要一堆P / Invoke代码。
internal static class NativeMethods
{
public const uint LVM_FIRST = 0x1000;
public const uint LVM_GETIMAGELIST = (LVM_FIRST + 2);
public const uint LVM_SETIMAGELIST = (LVM_FIRST + 3);
public const uint LVSIL_NORMAL = 0;
public const uint LVSIL_SMALL = 1;
public const uint LVSIL_STATE = 2;
public const uint LVSIL_GROUPHEADER = 3;
[DllImport("user32")]
public static extern IntPtr SendMessage(IntPtr hWnd,
uint msg,
uint wParam,
IntPtr lParam);
[DllImport("comctl32")]
public static extern bool ImageList_Destroy(IntPtr hImageList);
public const uint SHGFI_DISPLAYNAME = 0x200;
public const uint SHGFI_ICON = 0x100;
public const uint SHGFI_LARGEICON = 0x0;
public const uint SHGFI_SMALLICON = 0x1;
public const uint SHGFI_SYSICONINDEX = 0x4000;
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260 /* MAX_PATH */)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
[DllImport("shell32")]
public static extern IntPtr SHGetFileInfo(string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbSizeFileInfo,
uint uFlags);
[DllImport("uxtheme", CharSet = CharSet.Unicode)]
public static extern int SetWindowTheme(IntPtr hWnd,
string pszSubAppName,
string pszSubIdList);
}
现在,让我们使用它。出于演示目的,我将一个ListView控件添加到名为listView1
的表单中,将其设置为显示在" Details"模式,并将以下代码转储到表单的加载事件处理程序:
private void Form1_Load(object sender, EventArgs e)
{
// Obtain a handle to the system image list.
NativeMethods.SHFILEINFO shfi = new NativeMethods.SHFILEINFO();
IntPtr hSysImgList = NativeMethods.SHGetFileInfo("",
0,
ref shfi,
(uint)Marshal.SizeOf(shfi),
NativeMethods.SHGFI_SYSICONINDEX
| NativeMethods.SHGFI_SMALLICON);
Debug.Assert(hSysImgList != IntPtr.Zero); // cross our fingers and hope to succeed!
// Set the ListView control to use that image list.
IntPtr hOldImgList = NativeMethods.SendMessage(listView1.Handle,
NativeMethods.LVM_SETIMAGELIST,
NativeMethods.LVSIL_SMALL,
hSysImgList);
// If the ListView control already had an image list, delete the old one.
if (hOldImgList != IntPtr.Zero)
{
NativeMethods.ImageList_Destroy(hOldImgList);
}
// Set up the ListView control's basic properties.
// Put it in "Details" mode, create a column so that "Details" mode will work,
// and set its theme so it will look like the one used by Explorer.
listView1.View = View.Details;
listView1.Columns.Add("Name", 500);
NativeMethods.SetWindowTheme(listView1.Handle, "Explorer", null);
// Get the items from the file system, and add each of them to the ListView,
// complete with their corresponding name and icon indices.
string[] s = Directory.GetFileSystemEntries(@"C:\...");
foreach (string file in s)
{
IntPtr himl = NativeMethods.SHGetFileInfo(file,
0,
ref shfi,
(uint)Marshal.SizeOf(shfi),
NativeMethods.SHGFI_DISPLAYNAME
| NativeMethods.SHGFI_SYSICONINDEX
| NativeMethods.SHGFI_SMALLICON);
Debug.Assert(himl == hSysImgList); // should be the same imagelist as the one we set
listView1.Items.Add(shfi.szDisplayName, shfi.iIcon);
}
}
请注意,作为一个简单的示例程序,这几乎没有错误检查。我投入了一些Debug.Assert健全性检查,只是为了做好准备。
另外,为了完整起见,我继续使用代码使ListView看起来像Explorer使用的那样。这是由SetWindowTheme function处理的。
shell还有其他功能但你会在这里失踪。例如,shell覆盖 - 某些图标上出现的小徽章,例如已经检入源控制系统的徽章。这留给了读者的练习。 (提示:LVSIL_STATE
标志用于为ListView控件设置"状态"图像列表。)当然,他们在一天内没有编写资源管理器。