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

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

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

    class FileIcon
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEINFO
        {
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            public string szDisplayName;
            public string szTypeName;
        };
        const uint SHGFI_ICON = 0x00000100;
        const uint SHGFI_LARGEICON = 0x00000000;
        const uint SHGFI_SMALLICON = 0x00000001;
        const uint SHGFI_SYSICONINDEX = 0x4000;

        [DllImport("shell32.dll")]
        public static extern IntPtr SHGetFileInfo(string filename,uint fileattributes,ref SHFILEINFO shfi,uint cbfi,uint flag);

        [DllImport("user32.dll")]
        public static extern bool DestroyIcon(IntPtr hIcon);

        public static uint LVM_FIRST = 0x1000;
        public static uint LVM_SETIMAGELIST = LVM_FIRST + 3;
        public static uint LVSIL_NORMAL = 0;
        public static uint LVSIL_SMALL = 1;

        [DllImport("User32.DLL")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, IntPtr lParam);

        public static void ListViewSetImageList(System.Windows.Forms.ListView lv)
        {
            SHFILEINFO shfi = new SHFILEINFO();
            IntPtr hImageList;
            hImageList = SHGetFileInfo("", 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_LARGEICON | SHGFI_SYSICONINDEX);
            SendMessage(lv.Handle, LVM_SETIMAGELIST, LVSIL_NORMAL, hImageList);
            hImageList = SHGetFileInfo("", 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
            SendMessage(lv.Handle, LVM_SETIMAGELIST, LVSIL_SMALL, hImageList);
        }
        public static int GetIconIndex(string filename)
        {
            SHFILEINFO shfi = new SHFILEINFO();
            SHGetFileInfo(filename, 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_ICON | SHGFI_SMALLICON);
            return (int)shfi.iIcon;
        }
        public static Icon GetSmallIconFromFile(string filename)
        {
            SHFILEINFO shfi=new SHFILEINFO();
            SHGetFileInfo(filename, 0,ref shfi,(uint) Marshal.SizeOf(shfi), SHGFI_ICON | SHGFI_SMALLICON);
            Icon myicon =(Icon) Icon.FromHandle(shfi.hIcon).Clone();
            DestroyIcon(shfi.hIcon);
            return myicon;
        }
        public static Icon GetLargeIconFromFile(string filename)
        {
            SHFILEINFO shfi = new SHFILEINFO();
            SHGetFileInfo(filename, 0, ref shfi, (uint)Marshal.SizeOf(shfi), SHGFI_ICON | SHGFI_LARGEICON);
            Icon myicon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();
            DestroyIcon(shfi.hIcon);
            return myicon;
        }
    }
这里要注意的是,使用SHGetFileInfo函数,如果参数中有SHGFI_ICON,也就是获得了icon的句柄的话,要记得使用DestroyIcon释放句柄,msdn明确说明了这个,我在网上看到的代码都没有这一句。

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

        public Form1()
        {
            InitializeComponent();
            this.listView1.View = View.LargeIcon;
            this.listView1.SmallImageList = this.imageListSmall;
            this.listView1.LargeImageList = this.imageListLarge;
        }
        void RefreshFileList()
        {
            this.listView1.Items.Clear();
            this.imageListLarge.Images.Clear();
            this.imageListSmall.Images.Clear();
            int count = 0;
            DirectoryInfo dirInfo = new DirectoryInfo(this.textBox1.Text);
            
            if (dirInfo.Exists)
            {
                DirectoryInfo[] infos = dirInfo.GetDirectories();
                foreach (DirectoryInfo d in infos)
                {
                    ListViewItem lv = new ListViewItem(d.Name);
                    lv.ImageIndex = count;
                    listView1.Items.Add(lv);
                }
                if (infos.Length > 0)
                {
                    this.imageListSmall.Images.Add(FileIcon.GetSmallIconFromFile(""));
                    this.imageListLarge.Images.Add(FileIcon.GetLargeIconFromFile(""));
                    count++;
                }
            }

            string[] files = Directory.GetFiles(this.textBox1.Text);
            foreach (string str in files)
            {
                this.imageListLarge.Images.Add(FileIcon.GetLargeIconFromFile(str));
                this.imageListSmall.Images.Add(FileIcon.GetSmallIconFromFile(str));
                ListViewItem lv = new ListViewItem(Path.GetFileName(str));
                FileInfo fi = new FileInfo(str);
                string num = fi.Length.ToString();
                for (int i = num.Length - 3; i > 0; i -= 3)
                {
                    num = num.Insert(i, ",");
                }
                lv.SubItems.Add(num);
                lv.ImageIndex = count++;
                this.listView1.Items.Add(lv);
            }
        }

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

  

        public Form1()
        {
            InitializeComponent();
            this.listView1.View = View.LargeIcon;
            FileIcon.ListViewSetImageList(listView1);
        }

        void RefreshFileList()
        {
            this.listView1.Items.Clear();
            DirectoryInfo dirInfo = new DirectoryInfo(this.textBox1.Text);
            if (!dirInfo.Exists)
                return;
            DirectoryInfo[] infos = dirInfo.GetDirectories();
            int iDirIconIndex=FileIcon.GetIconIndex("");
            this.listView1.BeginUpdate();
            foreach (DirectoryInfo d in infos)
            {
                ListViewItem lv = new ListViewItem(d.Name);
                lv.ImageIndex = iDirIconIndex;
                listView1.Items.Add(lv);
            }
            string[] files = Directory.GetFiles(this.textBox1.Text);
            foreach (string str in files)
            {
                ListViewItem lv = new ListViewItem(Path.GetFileName(str));
                FileInfo fi = new FileInfo(str);
                string num = fi.Length.ToString();
                for (int i = num.Length - 3; i > 0; i -= 3)
                {
                    num = num.Insert(i, ",");
                }
                lv.SubItems.Add(num);
                lv.ImageIndex = FileIcon.GetIconIndex(str);
                this.listView1.Items.Add(lv);
            }
            this.listView1.EndUpdate();
        }

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

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值