Click here to Skip to main content
15,908,841 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I currently have a thread in a private class that runs and stores data in an array List variable, and main window class which has a thread to run and display the data from the array List variable into the Listview. However, it does not run correctly and nothing is being displayed on the Listview.

What I have tried:

On the main window class, I have the thread run like this:

C#
  private void startButton_Click(object sender, EventArgs e)
    {
        // Clear the results list:
        resultsList.Items.Clear();

        //The Start function is to run the thread in another class, Searcher class.
        //If the function runs, it returns a true boolean in this case.
        if (Searcher.Start(pars) == true)
        {
            DisableButtons();
            var threadParameters = new ThreadStart(delegate { CreateResultsListItem(); });
            var thread2 = new Thread(threadParameters);
            thread2.Start();
        }

    }

private void CreateResultsListItem()
{
    
        if (resultsList.InvokeRequired)
        {
            Action safeRun = delegate { CreateResultsListItem(); };
            resultsList.Invoke(safeRun);
        }
        else
        {
            Console.WriteLine("Thread 2");
            foreach (String filename in Searcher.Info_Kword)
            {
                // Create a new item and set its text:
                ListViewItem lvi = new ListViewItem();
                DateTime mod;
                long length;
                lvi.Text = filename.ToString();
                mod = File.GetLastWriteTime(filename);
                length = new FileInfo(filename).Length;
                lvi.SubItems.Add(length.ToString());
                lvi.SubItems.Add(mod.ToString());
                // Add the new item to the list:
                resultsList.Items.Add(lvi);

            }

            foreach (String file in Searcher.Info_Fnames)
            {

                // Create a new item and set its text:
                ListViewItem lvi2 = new ListViewItem();
                DateTime mod2;
                long length2;
                lvi2.Text = file.ToString();
                mod2 = File.GetLastWriteTime(file);
                length2 = new FileInfo(file).Length;
                lvi2.SubItems.Add(length2.ToString());
                lvi2.SubItems.Add(mod2.ToString());
                resultsList.Items.Add(lvi2);
            }
        }
}
Posted
Updated 8-Feb-22 10:22am
v4

You have a number of problems here: you are starting a thread which immediately checks if it is running on the original, UI thread, and if not calls Invoke.
Which moves the code back to the UI thread.

Which renders the whole idea of using a second thread to handle your long operation redundant, because the new thread runs for nanoseconds before terminating itself, while the long job runs in it's entirety on the UI thread ...

And you do this because only the UI thread can access UI controls.

Instead of this, use a BackgroundWorker class[^] instance to run your "long job" and report each new item back to the UI thread via the progress reporting mechanism (use the UserState property of the ProgressChangedEventArgs Class[^] to pass the data) and let that event handler update the actual display item.
 
Share this answer
 
As you've discovered, you cannot access UI controls from a non-UI thread. Which is why the first thing your CreateResultsListItem does is check whether it's running on a non-UI thread, and if so, invoke the method on the UI thread instead.

But that means your code does the following:
  1. On the UI thread, startButton_Click starts a new thread to call the CreateResultsListItem method;
  2. CreateResultsListItem finds that it's not running on the UI thread, and marshals the call back to the UI thread;
  3. CreateResultsListItem then runs a second time on the UI thread, blocking the UI until it has finished;
  4. The first call to CreateResultsListItem running on the background thread is notified that the second call has completed, and the new thread ends.

Hopefully you can see from that description that there is absolutely no benefit in starting a new thread to call CreateResultsListItem. In fact, it's actually a dangerous thing to do - if you decided to wait for the background thread to finish, you'd end up with a deadlock, and your application would hang indefinitely.

Just call the CreateResultsListItem method directly from the UI thread:
C#
private void startButton_Click(object sender, EventArgs e)
{
    resultsList.Items.Clear();
    if (Searcher.Start(pars) == true)
    {
        DisableButtons();
        CreateResultsListItem();
    }
}
 
Share this answer
 
Here is the official Microsoft documentation on this topic: How to make thread-safe calls to controls (Windows Forms .NET)

Here is an extract from the above link:
C#
private void button1_Click(object sender, EventArgs e)
{
    var threadParameters = new System.Threading.ThreadStart(delegate { WriteTextSafe("This text was set safely."); });
    var thread2 = new System.Threading.Thread(threadParameters);
    thread2.Start();
}

public void WriteTextSafe(string text)
{
    if (textBox1.InvokeRequired)
    {
        // Call this same method but append THREAD2 to the text
        Action safeWrite = delegate { WriteTextSafe($"{text} (THREAD2)"); };
        textBox1.Invoke(safeWrite);
    }
    else
        textBox1.Text = text;
}

The modern method is to use Async/Await Asynchronous programming:Asynchronous programming with async and await.
 
Share this answer
 
Comments
Richard Deeming 8-Feb-22 5:56am    
That's a really poor example from Microsoft. As both Griff and I pointed out, starting a new thread from the UI thread which does nothing other than marshalling the call back to the UI thread serves no purpose, and can cause deadlocks if you're not extremely careful.
Graeme_Grant 8-Feb-22 6:11am    
I do not disagree with you and Griff. What I am addressing is his general lack of understanding of handling threads.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900