【问题标题】:C# Show File and Folder Icons in ListviewC# 在 Listview 中显示文件和文件夹图标
【发布时间】:2016-06-13 13:32:44
【问题描述】:

我开发了一个类似于文件浏览器的应用程序。
有一个listview 显示目录中的文件和子文件夹。

如何在列表视图中显示文件和文件夹图标

以下方式,将文件路径添加到listview。我想显示图标:

string[] s = Directory.GetDirectories(file);
        foreach (string file in s)
        {
             listView1.Items.Add(file);

        }

【问题讨论】:

  • 一个非常广泛的问题。你想让我们教你如何为 LIstView 中的项目设置图标吗?要我们教你如何检索系统镜像列表吗?或者您想检索每个单独文件的图标?你想要什么尺寸的图标,大还是小?
  • @CodyGray,我想用适当的图标、小图标来表示每个文件和文件夹。谢谢。

标签: c# file listview icons directory


【解决方案1】:

有几种不同的方法可以做到这一点。一种方法是创建一个 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,将其设置为以“详细信息”模式显示,并将以下代码转储到表单的 Load 事件处理程序中:

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 还可以做一些其他的事情,但是你会在这里遗漏。例如,外壳覆盖——出现在某些图标上的小徽章,比如那些已经被检查到源代码控制系统中的图标。这留给读者作为练习。 (提示:LVSIL_STATE 标志用于为 ListView 控件设置“状态”图像列表。)当然,他们不是一天写的 Explorer。

【讨论】:

  • 感谢您的时间和精力。在尝试之前,我已经将您的答案作为已接受的答案。还有一件事:您能否展示一下您一开始提到的简短方法:使用 imageList?提前致谢!
  • @user 这并不是真的更短......实际上大部分代码都是相同的。如果您真的需要,我可以将其添加到我的答案中,但基本上,您创建一个 ImageList 对象并以正常方式将其分配给您的 ListView。然后,在 foreach 循环中,使用 SHGFI_ICONSHGFI_SMALLICON 标志调用 NativeMethods.SHGetFileInfo 函数。这将填充shfi.hIcon 成员。您使用它来创建一个图标 (Icon.FromHandle(shfi.hIcon)),将生成的 Icon 对象添加到您的 ImageList 对象,最后通过 P/Invoking DestroyIcon 删除 shfi.hIcon(以防止泄漏)。
  • 基本上不同的是,一种方式是您一次获取系统图像列表,然后对SHGetFileInfo 的后续调用将返回该图像列表中图标的索引。另一种方式,您自己管理图像列表,调用SHGetFileInfo 来获取实际图标的句柄,您需要将其添加到您的自我管理的图像列表中。
  • 这里最好的答案之一。我需要相同的功能,但对于 TreeView。相同的代码,只是 SendMessage 方法的常量不同。
  • 为什么这么多C#问题的答案都使用Dllimport来使用C++代码?真的没有原生解决方案吗?
猜你喜欢
  • 1970-01-01
  • 2019-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-12
  • 2016-10-13
相关资源
最近更新 更多