如何从拖动的文件中快速将大量图标加载到ListView中下降了吗?

时间:2016-07-29 01:24:06

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

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

在我的测试中,拖动了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资源管理器上,直到拖动& drop task已完全完成(循环遍历所有文件)。

这是一份粗略的草稿,可以直观地展示应用程序:

desktop launcher

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

1 个答案:

答案 0 :(得分:0)

我建议一步加载所有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;
}