0

Im currently trying to download all the files from my computer and display them in a list view. Im using a foreach statement for this.

However, when the list view updates the UI flickers alot and its really annoying.

Below is my code to update my UI:

if (text.StartsWith("fdrivel§")) //The client sent a list of drives
                {
                    String data = text.Split('§')[1]; //Get the drive listing

                    LvClear(listView3); //Clear the listView for drives

                    foreach (String drive in data.Split('\n')) //Loop through the drives
                    {
                        if (!drive.Contains("|")) continue; //If incorrect drive, then skip it
                        String name = drive.Split('|')[0]; //Get the label of the drive (C:, D:, E: etc.)
                        String size = Convert(drive.Split('|')[1]); //Get the total size of the drive

                        AddFileCallback(name, size, "N/A", name); //Update the UI
                    }
                }

This then calls an AddFileCallBack Method that does this:

private void AddFileCallback(String name, String size, String crtime, String path)
    {
        if (this.InvokeRequired) //If we need to invoke
        {
            addFile callback = new addFile(AddFileCallback); //Create a callback
            this.Invoke(callback, new object[] { name, size, crtime, path }); //Invoke the callback
        }
        else
        {
            ListViewItem lvi = new ListViewItem
            {
                Text = name //Set the entry name
            }; //Create a new listView item
            lvi.SubItems.Add(size); //Set the entry size
            lvi.SubItems.Add(crtime); //Set the entry creation time
            lvi.SubItems.Add(path); //Set the entry full path
            listView3.Items.Add(lvi); //Add the item to the list
            listView3.Items[0].Selected = true; //Select the first item of the list
        }
    }

Is want it to still load each file one at a time in the ui but I don't want it to flicker.

Wondering if someone could help me.

Thankyou in advance.

Jeremy Griffin
  • 107
  • 1
  • 8
  • Have you looked at the `Control.SuspendLayout` method? https://msdn.microsoft.com/en-us/library/system.windows.forms.control.suspendlayout(v=vs.110).aspx – JuanR Apr 18 '18 at 14:57
  • Have you tried [`BeginUpdate()`](https://msdn.microsoft.com/en-us/library/system.windows.forms.listview.beginupdate%28v=vs.110%29.aspx) and [`EndUpdate`](https://msdn.microsoft.com/en-us/library/system.windows.forms.listview.endupdate(v=vs.110).aspx)? – Reza Aghaei Apr 18 '18 at 14:58
  • You could create all the LVIs you want, store them in a List and add them to the LV control at the end en masse. A DataGridView with a datasource would also flicker less because most additions would not require a paint – Ňɏssa Pøngjǣrdenlarp Apr 18 '18 at 15:07

1 Answers1

1

Part of the issue is that you are doing the right thing by using Multitasking (at least that is what a name like AddFileCallback implies).

Writing to a GUI is expensive. If you only do it once on a user Triggered event you will not notice it. But if you do in any form of loop - especially ones running in paralell, those costs will ad up quickly. I wrote a exmple to showcase this issue:

using System;
using System.Windows.Forms;

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

        int[] getNumbers(int upperLimit)
        {
            int[] ReturnValue = new int[upperLimit];

            for (int i = 0; i < ReturnValue.Length; i++)
                ReturnValue[i] = i;

            return ReturnValue;
        }

        void printWithBuffer(int[] Values)
        {
            textBox1.Text = "";
            string buffer = "";

            foreach (int Number in Values)
                buffer += Number.ToString() + Environment.NewLine;
            textBox1.Text = buffer;
        }

        void printDirectly(int[] Values){
            textBox1.Text = "";

            foreach (int Number in Values)
                textBox1.Text += Number.ToString() + Environment.NewLine;
        }

        private void btnPrintBuffer_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Generating Numbers");
            int[] temp = getNumbers(10000);
            MessageBox.Show("Printing with buffer");
            printWithBuffer(temp);
            MessageBox.Show("Printing done");
        }

        private void btnPrintDirect_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Generating Numbers");
            int[] temp = getNumbers(1000);
            MessageBox.Show("Printing directly");
            printDirectly(temp);
            MessageBox.Show("Printing done");
        }
    }
}

Your example is way worse, because the UI actually has time to run update between the updates. So there is even more processing being done as you itterate over hte list.

There are only a few solutions to such an issue:

  • UI Virtualsation. WPF has some decent support for that. I am unsure how much Windows Forms supports it. Or how much you can use it in a case where the elements that need to be displayed keep expanding.
  • Make sure this is not a machine Aberation. If the machine is missing a proper GPU driver (and in many cases if it is a Virtual Machine), that means the whole drawing work is done on the CPU (not the CPU integrated GPU; the actuall CPU). That can cause human visible flickering even when there is no fault in the Design. It is just that the CPU is that glacially slow ad doing the drawing work compared even a cheap GPU.
  • Limit how often the GUI updates to be less then/unrelated to how often data appears. There are many ways to do this. One I am fond off is having the callback write to a backing variable (rather then the UI itself). And having a timer that will Update the UI with the current values of said variable that often. 30-60 updates per seconds is usually the limit a user can porcess anyway. But all the ways the other posters already mentioned - like using AddRange, or manually disabling the UI updates for a spell.
Christopher
  • 9,634
  • 2
  • 17
  • 31