【问题标题】:How to load lots of icons into ListView quickly from files dragged & dropped?如何从拖放的文件中快速将大量图标加载到 ListView 中?
【发布时间】:2016-12-03 14:21:55
【问题描述】:

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

在我的测试中,拖了一个包含 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 资源管理器上的拖放集合图标仍然“挂起”。

这是一个粗略的草稿,用于展示应用程序的视觉效果:

(是的,我知道提取的图标看起来也很垃圾,但我认为这是与我遇到的缓慢问题不同的问题)

【问题讨论】:

    标签: c# winforms listview drag-and-drop


    【解决方案1】:

    我的建议是不要一次性加载所有 381 个图标,尤其是在项目不可见的情况下。当项目滚动到视图中时按需加载它们,如果视图足够大,请使用您选择的线程/任务/并发技术在后台加载它们。

    这就是 Windows 所做的。

    为了加快加载速度,您可能需要使用最有可能从缓存中受益的系统图像列表。这是获取大图标的一些代码。只需将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;
    }
    

    【讨论】:

    • 这些是拖到窗口的项目。此外,我可以一次在 Windows 资源管理器中看到所有 381 个带有图标的项目。即刻。 :)
    • 我第一次得到它,你不需要加粗“不”。我认为您错过的是,即使我分多个步骤加载它们 - 仍然需要花费大量时间来显示所有图标和信息。 Windows 资源管理器立即显示它们,如果你放慢速度,你可以看到 Windows 也一次显示它们 1,但速度要快得多。在 0.0001 秒内从文件加载 381 个图标,在 200 秒内从文件加载 381 个图标。
    • @SanuelJackson 我建议你仔细重新阅读what it was I actually changed 没有态度。作为一名经验丰富的 Windows Shell 开发人员,我可以向您保证,在显示 1000 个 Word 文件 的文件夹的图标和显示一个文件夹,显示 1000 个不同的应用程序可执行文件,每个都有自己的应用程序图标。 文件类型图标处理程序与应用程序图标处理程序不同。因此,为什么“Windows 资源管理器会立即将它们全部显示出来”
    • 没有给出“态度”,我在澄清,因为你似乎忽略了我所说的。这里真正的问题是每个图标获取每个文件需要 1 到 15 秒(刚刚测试过)。关于应用程序,完全相同的文件以其图标完全显示在 Windows 资源管理器中——不使用神秘或不同的东西,文件的测试用例完全相同,是的,我可以在一个视图中看到所有 381 个应用程序图标我的屏幕需要 Windows 资源管理器不到 1 秒的时间来显示全部
    • 很明显,上面从文件中读取图标的代码非常慢。所以作为一个~Windows Shell 开发者~也许你对这个问题很熟悉。我还知道,在非常大的列表中,图标会在屏幕外分页。
    猜你喜欢
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 2016-05-01
    • 2013-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-07
    • 2020-07-20
    相关资源
    最近更新 更多