Link to home
Start Free TrialLog in
Avatar of jeesrinu
jeesrinu

asked on

How to load images into windows forms or Listview as faster?

I am using Imagelist control for loading images and listview control is used to show images. I used Application.DoEvents() also for updating GUI. But The process is very slow. I need a approach to load images fastly as same as in Windows Movie Maker --> Import Pictures option. For loading 250 images my program takes 9 seconds, But movie maker takes just 2 seconds. Give me a suggestion. Thanks in advance.
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Can you show us your current loading code?

The most common method is to live with an SLOW first load but make thumbnails of the images and store them in the same folder with a different extension.  Then subsequent runs can load the smaller file and execute faster.  This way you only load the full size image when it is actually needed.
Avatar of jeesrinu
jeesrinu

ASKER


openFileDialog1.Multiselect = true;
            imageList1.ImageSize = new Size(100, 75);
            listView1.View = View.LargeIcon;
            listView1.Font = new Font("arail", 8);
            ListViewItem item;
            //listView1.Columns.Add("0","Nothing",80,HorizontalAlignment.Center,0);
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                btnClearlist.Enabled = false;
                lblDone.Text = "";
                lblDragPhotos.Visible = false;
                fileNames = openFileDialog1.FileNames;
                imgList.ImageSize = new Size(100, 75);
                imgList.ColorDepth = ColorDepth.Depth24Bit;
                //Bitmap bmp = new Bitmap(95, 75);
                //Graphics g = Graphics.FromImage(bmp);
                //Pen p = new Pen(Color.Gray);
                //g.FillRectangle(new SolidBrush(Color.LightGray), 0, 0, 100, 70);
                //g.DrawRectangle(p, 1, 1, 93, 73);
                //g.Dispose();
                foreach (string s in fileNames)
                {
                    try
                    {
                        if (imageList1.Images[s] is Image)
                        { }
                        else
                        {
                            //imgList.Images.Add(s,bmp);
                            listView1.LargeImageList = imgList;
                            item = new ListViewItem();
                            string str=System.IO.Path.GetFileName(s);
                            if (str.Length > 18)
                                item.Text = str.Substring(0, 15)+"...";
                            else
                                item.Text = str;
                            item.ImageKey = s;
                            listView1.Items.Add(item);
                        }
                    }
                    catch
                    {
                    }
                }
                lblPhotos.Text = "Total photos are " + listView1.Items.Count;
                foreach (string s in fileNames)
                {
                    try
                    {
                        if (imageList1.Images[s] is Image)
                        { }
                        else
                        {
                            Application.DoEvents();
                            Cursor.Current = Cursors.WaitCursor;
                            listView1.LargeImageList = imageList1;
                            //imgList.Images[imgList.Images.IndexOfKey(s)] = imageList1.Images[imageList1.Images.IndexOfKey(s)];
                            imageList1.Images.Add(s, Image.FromFile(s));
                        }
                    }
                    catch
                    {
                    }
                }
                listView1.LargeImageList = imageList1;
                lblPhotos.Text = "Total photos are " + listView1.Items.Count;
                if (listView1.Items.Count != 0)
                {
                    lblDone.Text = "You can create now";
                    lblDone.Left = Convert.ToInt32((grpCollage.Width - lblDone.Width - 4) / 2);
                    btnClearlist.Enabled = true;
                    lblDragPhotos.Visible = false;
                }
            }

Open in new window

Did you see the code? Please give me replay....
Above my code is working dead slow. I need fast to load images into listview. Please give me replay. Thanks in advance.
The bottleneck is here:

    imageList1.Images.Add(s, Image.FromFile(s));

The whole image must be read in to make a thumbnail from it.  How big are the images?

As I said before, the best method is to make your own thumbnails and load those instead.
I used to load Thumbnails also but it took also same time for loading images into images list.
imageList1.Images.Add(s, Image.FromFile(s).GetThumbnailImage(100, 100,myCallback,IntPtr.Zero));
I used this one also before but speed there is no difference. ok leave it. Later i wil put multi threading. Actually my requirement is  while i am scrolling listview at that time loading images into listview should not be paused.i used Application.DoEvents() to update GUI. Is there any solution for this. Please let me know. If i will get this solution then put my concentration on speeding. If u didn't get my point please check the Windows Movie maker --> Import Pictures option. Thanks in advance.
I don't see an  "Import Pictures"  option in my copy of Movie Maker (Win7).  I do see "Add Videos and Photos"   Is that what you mean?
If so, then the answer is this:  
Display the OpenFIle dialog, having it pre-set to show Thumbnails.  That is, use the existing high-speed system provided by the OS to populate and display the listview.
i mean same what you said(Add Videos and photos). But i am not asking that what you gave the answer. when we click on add photos option to add photos  into the movie maker collections. That photos are loading faster into movie maker collection. When i am scrolling scroll bar of photos collection window  at the time of loading photos, The loading process could not be paused. That's good. How it's possible there? If u didn't get my point take photos more than 100 and import or add them into movie maker.Then you will see. While loading images you can scroll collection window scrollbar with out disturbance to your images loading.


 It's not possible in my program as listview loading images (ImagesList --> Listview). Here when we are scrolling listview, loading process will be paused up to we release the scrollbar. i am using Application.DoEvents() here to upadte GUI while loading images into listview(Imagelist-->listview).

My requirement is while loading images suppose we take more than 100 into list view from imagelist  (open file dialog-->imagelist), After ecry single image loading into RAM, the image should be appear in Listview. For this i am using Application.DoEvents(). If you didn't get see my above code. At the time of loading when scroll listview ,the loading process is paused. That's i no need. I hope you will catch my point. Thanks in advance.
So you want scrolling in your own listview to be fast...
I don't know what MovieMaker does or how Explorer shows even a very large list of  thumbnails so quickly.
My guess is that they use LVS_OWNERDRAWFIXED and display images manually.  For high speed, I would suggest using a worker thread that created a cache of thumbnails.  Draw the first few items immediately, and then the thread would prepare the rest so they'd be ready for viewing when the user scrolls to that point.
In fact, that almost certainly how MovieMaker does it.  Try this:
Import several hundred items.  The first screen fills with images in a few seconds.  But if you quickly scroll down, you will see that MOST of the images are in "generic" form.
=-=-=-=-=-=-=-=-=-=-=-=-=-=
Another angle of attack:  
Pre-create an ImageList that is populated with a "generic" image for each item.  Then all you need to do is update the ImageList (rather than increase its length with each addition).
That might be a lot of work.  A possibe alternative:
Use Explorer rather than your own listview control.  Just move the images all into a particular directory, then use an explorer window to display them.
Pre-create an ImageList that is populated with a "generic" image for each item.  Then all you need to do is update the ImageList (rather than increase its length with each addition).
 
That is appeared in my above code.  Use explorer is not suit for my app.

I am also using background thread. But no need. because of Application.DoEvents(). This is helpful and same time this is my problem.

I need to load images dynamically but not static. Have u free try with my code then you will catch my point what i am facing. Thanks in advance.
I think that the only way to handle this is to create an image cache -- like the thumbs.db file that Explorer uses.  
Right now you must open each image file and create a  thumbnail.  That's going to take time even on the fastest processor.
If you saved a cache file of pre-thumbnailed images, then loading them the next time you need them would be considerably faster.  That will not help you fhte first time, but it's something.
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Every time i need to  import images into listview. so Thumbnail concept is not suitable for my requirement. My requirement is while loading images suppose we take more than 100 into list view from imagelist  (open file dialog-->imagelist), After every single image loading into RAM, the image should be appear in Listview. For this i am using Application.DoEvents() (update GUI). At the time of loading images into listview, I need to scroll. If i scroll listview, the loading process is paused up to leave focus from listview. This would happen in my program. Actually what i am  need when i am scrolling lisview, the loading process should not be paused. Please give me a suitable answer. Thanks in advance.
Try this out...it worked well for me on a folder having 700+ full sized wallpapers:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private class Thumbnail
        {
            public string fileName; // this is a full path filename!
            public Bitmap bmp;
        }

        private Dictionary<string, ListViewItem> ListViewItems = new Dictionary<string, ListViewItem>();
        private Size ImageSize = new Size(100, 75);

        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.Multiselect = true;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                button1.Enabled = false;

                imageList1.Images.Clear();
                imageList1.ImageSize = this.ImageSize;
                imageList1.ColorDepth = ColorDepth.Depth24Bit;
                
                listView1.View = View.LargeIcon;
                listView1.Font = new Font("Arial", 8);
                listView1.LargeImageList = imageList1;

                List<string> fileNames = new List<string>();
                fileNames.AddRange(openFileDialog1.FileNames);
                ListViewItems.Clear();

                // load up BLANK ListViewItems for each image
                listView1.Items.Clear();
                listView1.BeginUpdate();
                foreach (string fileName in fileNames)
                {
                    ListViewItem lvi = listView1.Items.Add(System.IO.Path.GetFileNameWithoutExtension(fileName));
                    lvi.Tag = fileName;
                    ListViewItems.Add(fileName, lvi);
                }
                listView1.EndUpdate();

                // generate the thumbnails in the background thread
                BackgroundWorker bgw = new BackgroundWorker();
                bgw.WorkerReportsProgress = true;
                bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
                bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
                bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                bgw.RunWorkerAsync(fileNames);
            }
        }

        void bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bgw = (BackgroundWorker)sender;

            Bitmap bmp;
            PictureBox pb = new PictureBox();
            pb.SizeMode = PictureBoxSizeMode.Zoom;
            pb.Size = this.ImageSize;
            
            List<string> fileNames = (List<string>)e.Argument;
            foreach (string fileName in fileNames)
            {
                try
                {
                    // create a thumbnail using an invisible picturebox
                    pb.Image = Image.FromFile(fileName);
                    bmp = new Bitmap(this.ImageSize.Width, this.ImageSize.Height);
                    pb.DrawToBitmap(bmp, pb.ClientRectangle);
                    
                    // pass the thumbnail with its name out to the main UI
                    Thumbnail tb = new Thumbnail();
                    tb.fileName = fileName;
                    tb.bmp = bmp;
                    bgw.ReportProgress(-1, tb);
                    pb.Image = null;

                    // throttle the image loading background thread so it doesn't overload the main UI
                    // YES...this was necessary!  Otherwise the main UI eventually got bogged down tyring
                    // to update with the new thumbnails being generated...
                    System.Threading.Thread.Sleep(50); 
                }
                catch (Exception ex)
                {
                    MessageBox.Show("FileName: " + fileName + "\r\n\r\n" + ex.ToString(), "Error Loading Image", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

            }
        }

        void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // add the new thumbnail to the Listview
            Thumbnail tb = (Thumbnail)e.UserState;
            imageList1.Images.Add(tb.fileName, tb.bmp);
            ListViewItems[tb.fileName].ImageKey = tb.fileName;
        }

        void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show(listView1.Items.Count + " Image(s) Imported.", "Import Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
            button1.Enabled = true;            
        }

    }
}

Open in new window

I feel happy. You are really Genius. You got my point. The code what you sent is exactly suitable for my requirement. It's  working fine.

I have to need the same experience using  Multi threading to increase the speed twice or thrice for loading images into listview. If you feel free give me suggestion please. Thanks in advance.

I have to need the same experience using  Multi threading to increase the speed twice or thrice for loading images into listview. If you feel free give me suggestion please. Thanks in advance.
Using the .Net native image handling methods I don't know how to make it any faster (other than pre-building thumbnails for the NEXT run).  Adding more threads won't help as you'll end up flooding the UI and making it un-responsive again.

It might be possible using external Win APIs but I've never done it...

hjk