c#使用系统ImageList显示文件图标

今天在百度知道上看到一个提问,要做个简单的文件浏览器,于是拿出以前写的半成品修改了一下,中间碰到了一点小麻烦。主要是如何在c#里面给ListView控件设置图标时使用系统的ImageList,而不是自己创建ImageList对象。

先把封装了提取图标等API的FileIcon类贴出来。

[csharp]  view plain copy
  1. class FileIcon  
  2. {  
  3.     [StructLayout(LayoutKind.Sequential)]  
  4.     public struct SHFILEINFO  
  5.     {  
  6.         public IntPtr hIcon;  
  7.         public int iIcon;  
  8.         public uint dwAttributes;  
  9.         public string szDisplayName;  
  10.         public string szTypeName;  
  11.     };  
  12.     const uint SHGFI_ICON = 0x00000100;  
  13.     const uint SHGFI_LARGEICON = 0x00000000;  
  14.     const uint SHGFI_SMALLICON = 0x00000001;  
  15.     const uint SHGFI_SYSICONINDEX = 0x4000;  
  16.   
  17.     [DllImport("shell32.dll")]  
  18.     public static extern IntPtr SHGetFileInfo(string filename,uint fileattributes,ref SHFILEINFO shfi,uint cbfi,uint flag);  
  19.   
  20.     [DllImport("user32.dll")]  
  21.     public static extern bool DestroyIcon(IntPtr hIcon);  
  22.   
  23.     public static uint LVM_FIRST = 0x1000;  
  24.     public static uint LVM_SETIMAGELIST = LVM_FIRST + 3;  
  25.     public static uint LVSIL_NORMAL = 0;  
  26.     public static uint LVSIL_SMALL = 1;  
  27.   
  28.     [DllImport("User32.DLL")]  
  29.     public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, IntPtr lParam);  
  30.   
  31.     public static void ListViewSetImageList(System.Windows.Forms.ListView lv)  
  32.     {  
  33.         SHFILEINFO shfi = new SHFILEINFO();  
  34.         IntPtr hImageList;  
  35.         hImageList = SHGetFileInfo("", 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_LARGEICON | SHGFI_SYSICONINDEX);  
  36.         SendMessage(lv.Handle, LVM_SETIMAGELIST, LVSIL_NORMAL, hImageList);  
  37.         hImageList = SHGetFileInfo("", 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_SMALLICON | SHGFI_SYSICONINDEX);  
  38.         SendMessage(lv.Handle, LVM_SETIMAGELIST, LVSIL_SMALL, hImageList);  
  39.     }  
  40.     public static int GetIconIndex(string filename)  
  41.     {  
  42.         SHFILEINFO shfi = new SHFILEINFO();  
  43.         SHGetFileInfo(filename, 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_ICON | SHGFI_SMALLICON);  
  44.         return (int)shfi.iIcon;  
  45.     }  
  46.     public static Icon GetSmallIconFromFile(string filename)  
  47.     {  
  48.         SHFILEINFO shfi=new SHFILEINFO();  
  49.         SHGetFileInfo(filename, 0,ref shfi,(uint) Marshal.SizeOf(shfi), SHGFI_ICON | SHGFI_SMALLICON);  
  50.         Icon myicon =(Icon) Icon.FromHandle(shfi.hIcon).Clone();  
  51.         DestroyIcon(shfi.hIcon);  
  52.         return myicon;  
  53.     }  
  54.     public static Icon GetLargeIconFromFile(string filename)  
  55.     {  
  56.         SHFILEINFO shfi = new SHFILEINFO();  
  57.         SHGetFileInfo(filename, 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_ICON | SHGFI_LARGEICON);  
  58.         Icon myicon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();  
  59.         DestroyIcon(shfi.hIcon);  
  60.         return myicon;  
  61.     }  
  62. }  
这里要注意的是,使用SHGetFileInfo函数,如果参数中有SHGFI_ICON,也就是获得了icon的句柄的话,要记得使用DestroyIcon释放句柄,msdn明确说明了这个,我在网上看到的代码都没有这一句。

我开始写的代码是ListView.Items每添加一项,对应的SmallImageList和LargeImageList都用SHGetFileInfo获取一个图标并添加到ImageList,下面贴出这部分的代码。

[csharp]  view plain copy
  1. public Form1()  
  2. {  
  3.     InitializeComponent();  
  4.     this.listView1.View = View.LargeIcon;  
  5.     this.listView1.SmallImageList = this.imageListSmall;  
  6.     this.listView1.LargeImageList = this.imageListLarge;  
  7. }  
  8. void RefreshFileList()  
  9. {  
  10.     this.listView1.Items.Clear();  
  11.     this.imageListLarge.Images.Clear();  
  12.     this.imageListSmall.Images.Clear();  
  13.     int count = 0;  
  14.     DirectoryInfo dirInfo = new DirectoryInfo(this.textBox1.Text);  
  15.       
  16.     if (dirInfo.Exists)  
  17.     {  
  18.         DirectoryInfo[] infos = dirInfo.GetDirectories();  
  19.         foreach (DirectoryInfo d in infos)  
  20.         {  
  21.             ListViewItem lv = new ListViewItem(d.Name);  
  22.             lv.ImageIndex = count;  
  23.             listView1.Items.Add(lv);  
  24.         }  
  25.         if (infos.Length > 0)  
  26.         {  
  27.             this.imageListSmall.Images.Add(FileIcon.GetSmallIconFromFile(""));  
  28.             this.imageListLarge.Images.Add(FileIcon.GetLargeIconFromFile(""));  
  29.             count++;  
  30.         }  
  31.     }  
  32.   
  33.     string[] files = Directory.GetFiles(this.textBox1.Text);  
  34.     foreach (string str in files)  
  35.     {  
  36.         this.imageListLarge.Images.Add(FileIcon.GetLargeIconFromFile(str));  
  37.         this.imageListSmall.Images.Add(FileIcon.GetSmallIconFromFile(str));  
  38.         ListViewItem lv = new ListViewItem(Path.GetFileName(str));  
  39.         FileInfo fi = new FileInfo(str);  
  40.         string num = fi.Length.ToString();  
  41.         for (int i = num.Length - 3; i > 0; i -= 3)  
  42.         {  
  43.             num = num.Insert(i, ",");  
  44.         }  
  45.         lv.SubItems.Add(num);  
  46.         lv.ImageIndex = count++;  
  47.         this.listView1.Items.Add(lv);  
  48.     }  
  49. }  

使用这种方法,小规模的文件显示是没问题的,但是如果一次显示很多文件肯定会卡,甚至内存不足。在网上搜了一下发现SHGetFileInfo函数使用SHGFI_SYSICONINDEX参数可以返回系统的ImageList的句柄,在vc里面可以直接使用,但是c#的ListView控件的Handle属性是只读的,而且也没有提供类似FromHandle之类的方法。苦恼之余继续上网搜索,终于找到一篇文章, (链接:http://www.cnblogs.com/parry/archive/2007/10/23/935026.html) 作者使用SendMessage直接向ListView控件发送设置图像列表的消息,参考了这种方法我很快改进了我的代码。

  

[csharp]  view plain copy
  1. public Form1()  
  2. {  
  3.     InitializeComponent();  
  4.     this.listView1.View = View.LargeIcon;  
  5.     FileIcon.ListViewSetImageList(listView1);  
  6. }  
  7.   
  8. void RefreshFileList()  
  9. {  
  10.     this.listView1.Items.Clear();  
  11.     DirectoryInfo dirInfo = new DirectoryInfo(this.textBox1.Text);  
  12.     if (!dirInfo.Exists)  
  13.         return;  
  14.     DirectoryInfo[] infos = dirInfo.GetDirectories();  
  15.     int iDirIconIndex=FileIcon.GetIconIndex("");  
  16.     this.listView1.BeginUpdate();  
  17.     foreach (DirectoryInfo d in infos)  
  18.     {  
  19.         ListViewItem lv = new ListViewItem(d.Name);  
  20.         lv.ImageIndex = iDirIconIndex;  
  21.         listView1.Items.Add(lv);  
  22.     }  
  23.     string[] files = Directory.GetFiles(this.textBox1.Text);  
  24.     foreach (string str in files)  
  25.     {  
  26.         ListViewItem lv = new ListViewItem(Path.GetFileName(str));  
  27.         FileInfo fi = new FileInfo(str);  
  28.         string num = fi.Length.ToString();  
  29.         for (int i = num.Length - 3; i > 0; i -= 3)  
  30.         {  
  31.             num = num.Insert(i, ",");  
  32.         }  
  33.         lv.SubItems.Add(num);  
  34.         lv.ImageIndex = FileIcon.GetIconIndex(str);  
  35.         this.listView1.Items.Add(lv);  
  36.     }  
  37.     this.listView1.EndUpdate();  
  38. }  

使用这种方法比原来少使用了两个ImageList控件,节省了原来重复复制图标的开销,代码也更简洁了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值