Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Show File and Folder Icons in Listview

I develop an application that works like file browser.
There is a listview that shows the files and sub folders within a directory.

How can I show Files and Folders icons in listview?

The following way, adds the file path to the listview. I would like to show icons:

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

        }
like image 370
user3165438 Avatar asked Jun 13 '16 13:06

user3165438


1 Answers

There are several different ways to do this. One way is to create an ImageList and link it to your ListView, then retrieve the icon for each individual file, add it to your ImageList, and set the ListViewItem to display the icon at the appropriate index in your ImageList.

Another way would be to take advantage of the system image list, which is maintained by the shell. This would avoid the need to maintain duplicate copies of the icons yourself. Imagine if you have a bunch of folders in your ListView. All of them will have the same icon, but without a bunch of special care, you'd be saving as many copies of that folder icon as you are displaying items that use it. The shell implementation takes care of all of this duplicate-tracking for you. The system image list contains only the icons that are needed (the ones you explicitly request), and then only contains a single copy of each. I think this is a cleaner and more elegant design, so let's implement it. We'll need a bunch of P/Invoke code.

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);
}

And now, let's use it. For demonstration purposes, I added a ListView control to a form, named listView1, set it to display in "Details" mode, and dumped the following code into the form's Load event handler:

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);
   }
}

Note that, as a simple sample program, this does little to no error checking. I threw in some Debug.Assert sanity checks just for good measure.

Also, for completeness, I went ahead and threw in code to make the ListView look like the one Explorer uses. That's handled by the SetWindowTheme function.

There are other things that the shell does but that you will be missing here. For example, shell overlays—the little badges that appear on certain icons, like those that have been checked into a source control system. That's left as an exercise for the reader. (Hint: the LVSIL_STATE flag is used to set a "state" image list for a ListView control.) They didn't write Explorer in a day, of course.

like image 124
Cody Gray Avatar answered Oct 16 '22 10:10

Cody Gray