首页 文章

如何从拖放文件中快速将大量图标加载到ListView中?

提问于
浏览
0

我想从拖动到列表视图的多个文件中加载图标以及这些文件的一些信息,但过程进展非常缓慢 .

在我的测试中,拖动了381个文件的列表(有些不是exe,所以在我的代码中跳过了这些文件),从文件中加载图标需要2分钟,然后将它们添加到列表视图中 .

以下是我的代码的浓缩版本:

public Form1()
    {
        InitializeComponent();
        listView1.DragEnter += ListView1_DragEnter;
        listView1.DragDrop += ListView1_DragDrop;
        listView1.LargeImageList = new ImageList() { ImageSize = new Size( 64, 64) };
        listView1.SmallImageList = new ImageList();
        listView1.AllowDrop = true;
    }

    private void ListView1_DragDrop(object sender, DragEventArgs e)
    {
        if(e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            string[] values = (string[])e.Data.GetData(DataFormats.FileDrop);
            Convert.IconExtractor i = new Convert.IconExtractor();
            foreach (var v in values)
            {
                var info = new FileInfo(v);
                if(info.Extension.ToLower() == ".exe")
                {
                    ListViewItem item = new ListViewItem();
                    listView1.LargeImageList.Images.Add(i.Extract(info.FullName, Convert.IconSize.Large));
                    listView1.SmallImageList.Images.Add(i.Extract(info.FullName, Convert.IconSize.Small));
                    item.Text = Path.GetFileNameWithoutExtension(info.FullName);
                    item.ImageIndex = listView1.SmallImageList.Images.Count -1;
                    item.Tag = info.FullName;
                    listView1.Items.Add(item);
                }
            }
            listView1.Refresh();
        }
    }

    private void ListView1_DragEnter(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            e.Effect = DragDropEffects.All;
        }
    }

为方便起见,粘贴了用于提取的方法:

public enum IconSize
    {
        Small,
        Large
    }

    public class IconExtractor
    {

        //----------------------------------------------------------------------------
        //
        // Description: Extracts the icon associated with any file on your system.
        // Author: WidgetMan http://softwidgets.com
        //
        // Remarks...
        //
        // Class requires the IconSize enumeration that is implemented in this
        // same file. For best results, draw an icon from within a control's Paint
        // event via the e.Graphics.DrawIcon method.
        //
        //----------------------------------------------------------------------------

        private const int SHGFI_ICON = 0x100;
        private const int SHGFI_SMALLICON = 0x1;

        private const int SHGFI_LARGEICON = 0x0;
        private struct SHFILEINFO
        {

            public IntPtr hIcon;
            public int iIcon;

            public int dwAttributes;
            [VBFixedString(260), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]

            public string szDisplayName;
            [VBFixedString(80), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]

            public string szTypeName;
        }
        [DllImport("shell32", EntryPoint = "SHGetFileInfoA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

        private static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int ByValcbFileInfo, int uFlags);

        public IconExtractor()
        {
        }

        public System.Drawing.Icon Extract(string File, IconSize Size)
        {
            SHFILEINFO aSHFileInfo = default(SHFILEINFO);
            int cbFileInfo = 0;
            int uflags = 0;
            System.Drawing.Icon Icon = default(System.Drawing.Icon);

            switch (Size)
            {
                case IconSize.Large:
                    uflags = SHGFI_ICON | SHGFI_LARGEICON;
                    break;
                default:
                    uflags = SHGFI_ICON | SHGFI_SMALLICON;
                    break;
            }

            cbFileInfo = Marshal.SizeOf(aSHFileInfo);

            SHGetFileInfo(File, 0, ref aSHFileInfo, cbFileInfo, uflags);

            Icon = System.Drawing.Icon.FromHandle(aSHFileInfo.hIcon);

            return Icon;
        }

        public System.Drawing.Icon Extract(string File)
        {
            return this.Extract(File, IconSize.Small);
        }

    }
}

我该怎么做才能快速完成这个过程 . 这个概念是为多个应用程序制作一个快速启动器 .

另外值得注意的是,当这个过程发生时,拖动收集图标仍然在Windows资源管理器上“挂起”,直到拖放任务完全完成(循环遍历所有文件) .

这是一个粗略的草案,给出了应用程序的视觉效果:

desktop launcher

(是的,我知道提取的图标看起来也像废话,但我认为这是一个与我所遇到的缓慢问题不同的问题)

1 回答

  • 0

    我的建议是 not 一步加载所有381个图标,特别是如果项目不可见 . 当项目滚动到视图中时按需加载它们,如果视图足够大,则使用您选择的线程/任务/并发技术在后台加载它们 .

    这就是Windows的功能 .

    为了加快加载速度,您可能希望使用最有可能受益于缓存的 System Image List . 这是获取大图标的一些代码 . 只需将 size 更改为 SHGFI_ICON 供您使用 .

    public static Icon GetLargeIcon(string FileName, bool jumbo, bool useFileAttributes=false)
    {
        var shinfo = new SHFILEINFO();
    
    
        uint flags;
        flags = SHGFI_SYSICONINDEX;
    
        if (useFileAttributes) 
        {
            flags |= SHGFI_USEFILEATTRIBUTES;
        }
    
        var res = SHGetFileInfo(FileName, FILE_ATTRIBUTE_NORMAL, ref shinfo, (uint) Marshal.SizeOf(shinfo), flags);
        if (res == IntPtr.Zero)
        {
            throw (new FileNotFoundException());
        }
    
        var iconIndex = shinfo.iIcon;
    
        // Get the System IImageList object from the Shell:
        var iidImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
    
        IImageList iml;
        var size = jumbo
            ? SHIL_JUMBO 
            : SHIL_EXTRALARGE;
        var hres = SHGetImageList(size,  iidImageList, out iml); 
        if (hres != 0)
        {
            throw (new Exception("Error SHGetImageList"));
        }
    
        IntPtr hIcon;
        const int ILD_TRANSPARENT = 1;
        hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, out hIcon);
    
        var icon = Icon.FromHandle(hIcon);
        icon = icon.Clone() as Icon;
        var bm = icon.ToBitmap();
        DestroyIcon(hIcon);
    
        return icon;
    }
    

相关问题