Link to home
Start Free TrialLog in
Avatar of pashcroft
pashcroft

asked on

Most Efficient way to refresh ListViews (Windows.Forms)

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.
Avatar of eternal_21
eternal_21

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?
ASKER CERTIFIED SOLUTION
Avatar of eternal_21
eternal_21

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
Avatar of pashcroft

ASKER

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).
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);
            }
      }
}
> 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.