Solved

Most Efficient way to refresh ListViews (Windows.Forms)

Posted on 2004-09-01
5
363 Views
Last Modified: 2008-03-17
Whats the most efficient way to refresh a ListView control in Windows.Forms that is reflecting the file system.

I.e. I currently have a listview that i populate with files from a directory.  A timer keeps updating the listview to add listviewitems to represent files that appear on the file system and aren't yet in the listview, and to remove items that reflect files that are no longer on the file system.

I am discovering that when there are a lot of files in the directory, there is a massive performance hit that i really want to eliminate or at least minimise.

My current (inefficient) code centers around two methods, one to add new files the other to clear removed files, that might need the following clarifications:
- The xmlnode is part of the strategy i use to populate tree and list views dynamically - so you don't have to worry about understanding that.
- createFileItem(node, file) creates a new listviewitem that represents the file on the file system.
- IsFile((string)item.Tag) tells me if a listviewitem represents a file on the filesystem

private void insertAddedFiles(ListView listView, XmlNode node, string[] files)
{
      foreach (string file in files)
      {
            bool present = false;
            foreach (ListViewItem item in listView.Items)
            {
                  if (item.SubItems[0].Text == Path.GetFileName(file))
                  {
                        present = true;
                        break;
                  }
            }
            if (!present)
            {
                  ListViewItem item = createFileItem(node, file);
                  if (item != null)
                        listView.Items.Add(item);
            }
      }
}

private void clearRemovedFiles(ListView listView, string directory)
{
      foreach (ListViewItem item in listView.Items)
      {
            if (IsFile((string)item.Tag) && !File.Exists(Path.Combine(directory, item.SubItems[0].Text)))
                  item.Remove();
      }
}

I plan to try out your suggestions and will award points primarily to the most efficient, but I also think elegance is important.
0
Comment
Question by:pashcroft
  • 3
  • 2
5 Comments
 
LVL 10

Expert Comment

by:eternal_21
ID: 11959855
Questions:

  1. Does the ListView contents match the current files in a single specific directory?  I mean, if we have 100 files in a folder, does the ListView contain exactly 100 items?

  2. It is important that you track file creations and deletions as opposed to just reading the directory and generating a new ListView?

  3. How time-consuming is the createFileItem(XmlNode, string) method?
0
 
LVL 10

Accepted Solution

by:
eternal_21 earned 500 total points
ID: 11959986
Anyway, without the answers to the above questions, here is some optimized code:

    System.Collections.SortedList sortedList = new SortedList();

    private void insertAddedFiles(ListView listView, XmlNode node, string[] files)
    {
      foreach(string file in files)
        if(!sortedList.Contains(file))
        {
          ListViewItem item = createFileItem(node, file);
          if (item != null)
          {
            listView.Items.Add(item);
            sortedList.Add(file, item);
          }
        }
    }

    private void clearRemovedFiles(ListView listView, string directory)
    {
      System.Collections.ArrayList dirList = new System.Collections.ArrayList();
      dirList.AddRange(System.IO.Directory.GetFiles(directory));

      string[] listFiles = new string[sortedList.Keys.Count];
      sortedList.Keys.CopyTo(listFiles, 0);
      foreach(string file in listFiles)
        if(!dirList.Contains(file))
        {
          ((ListViewItem)sortedList[file]).Remove();
          sortedList.Remove(file);
        }
    }
0
 
LVL 4

Author Comment

by:pashcroft
ID: 11960303
Answers for eternal:

  1. Does the ListView contents match the current files in a single specific directory?  I mean, if we have 100 files in a folder, does the ListView contain exactly 100 items?

yes and no: the listview is actually filtered on multiple patterns, but it will show all the files match for match (eg: i'm showing for one directory *.log, *.csv and info.txt patterns, so the list will show the 12 .log files, the 2 csv files and the one info.txt file, but wont show any other files)

  2. It is important that you track file creations and deletions as opposed to just reading the directory and generating a new ListView?

yes.  i want (need?) list view to stay same unless a file:
- changes name (new listitem added, old list item removed),
- gets created/moved in (new listitem added) or
- gets deleted/moved out (old list item removed)

  3. How time-consuming is the createFileItem(XmlNode, string) method?

efficient as can be (uses a fileinfo object to retrieve filename, size and date to create a listitem) - but i still want to avoid just recreating all the listitems (because drawing them is noticable).
0
 
LVL 4

Author Comment

by:pashcroft
ID: 11960564
I've just reworked eternal's suggestion (see below), and it seems to be working nicely (reducing 100% cpu to around 25%)

I've opted for straight arrays where i could (less overhead) and gone for a hashtable over a sortedlist (no real need to sort)

Eternal, let me know what you think.

Everyone else, i'll be awarding to eternal unless anyone can significantly improve on this:

I'll be adding another question on the most efficient way to compare two arrays (C#) - so check for that one too :)

private Hashtable _fileSystemContents = new Hashtable();

private void insertAddedFiles(ListView listView, XmlNode node, string[] files)
{
      foreach (string file in files)
      {
            if (!_fileSystemContents.ContainsKey(file))
            {
                  ListViewItem item = createFileItem(node, file);
                  if (item != null)
                  {
                        listView.Items.Add(item);
                        _fileSystemContents.Add(file, item);
                  }
            }
      }
}

private void clearRemovedFiles(ListView listView, string directory)
{
      string[] files = Directory.GetFiles(directory);
      string[] keys = new string[_fileSystemContents.Count];
      _fileSystemContents.Keys.CopyTo(keys, 0);
      foreach (string key in keys)
      {
            if (Array.BinarySearch(files, key) < 0)
            {
                  listView.Items.Remove((ListViewItem)_fileSystemContents[key]);
                  _fileSystemContents.Remove(key);
            }
      }
}
0
 
LVL 10

Expert Comment

by:eternal_21
ID: 11964807
> I've opted for straight arrays where i could (less overhead) and gone for a hashtable over a sortedlist (no real need to sort)

Yes, I meant to change that... a HashTable will be faster than a SortedList!

> but i still want to avoid just recreating all the listitems (because drawing them is noticable)

If drawing the items is the problem, try using ListView.BeginUpdate() and then add/remove your items.  When you are finished call ListView.EndUpdate() and your ListView will be updated.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction                                                 Was the var keyword really only brought out to shorten your syntax? Or have the VB language guys got their way in C#? What type of variable is it? All will be revealed.   Also called…
Article by: Ivo
C# And Nullable Types Since 2.0 C# has Nullable(T) Generic Structure. The idea behind is to allow value type objects to have null values just like reference types have. This concerns scenarios where not all data sources have values (like a databa…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, just open a new email message. In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…

895 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now