Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Populating a listview from another thread

I'm trying to populate a listview from another class, but I'm geting this error: " Cross-thread operation not valid: Control 'listView1' accessed from a thread other than the thread it was created on. "

In my class I declare my listview like this:

class CheckBlankPages
{

    public String[] pdfFiles
    { get; set; }

    ListView _ListVireRef;
    public int NrCRT = 1;


    public CheckBlankPages(String[] pdfFiles = null, ListView listView = null)
    {
        this.pdfFiles = pdfFiles;
        _ListVireRef = listView;

    }
    public void StartCheckingPDF()
    {
        foreach (string pdf in pdfFiles)
        {
            String[] itm = { (NrCRT++).ToString(), pdf };
            ListViewItem item = new ListViewItem(itm);
            _ListVireRef.Items.Add(item);
        }
    }
}

and in my MainForm I use this code:

DialogResult rezultat = openFileDialog1.ShowDialog();
        if (rezultat == DialogResult.OK)
        {

            CheckBlankPages ck = new CheckBlankPages(openFileDialog1.FileNames, listView1);
            Thread CheckPDFs = new Thread(new ThreadStart(ck.StartCheckingPDF));
            CheckPDFs.Start();
        }

What is wrong?

like image 950
XandrUu Avatar asked May 24 '26 23:05

XandrUu


2 Answers

Usually I'm doing it like this:

using System;
using System.Windows.Forms;

namespace TestWinFormsThreding
{
    class TestFormControlHelper
    {
        delegate void UniversalVoidDelegate();

        /// <summary>
        /// Call form control action from different thread
        /// </summary>
        public static void ControlInvoke(Control control, Action function)
        {
            if (control.IsDisposed || control.Disposing)
                return;

            if (control.InvokeRequired)
            {
                control.Invoke(new UniversalVoidDelegate(() => ControlInvoke(control, function)));
                return;
            }
            function();
        }
    }

    public partial class TestMainForm : Form
    {
    // ...
    // This will be called from thread not the same as MainForm thread
    private void TestFunction()
    {
        TestFormCotrolHelper.ControlInvoke(listView1, () => listView1.Items.Add("Test"));
    }   
    //...
    }
}
like image 169
Telanar Avatar answered May 26 '26 12:05

Telanar


A simple search here on SO would have brought up many results that tell you that it is not allowed to change a GUI control from a thread other than the thread which created the control (cross-thread GUI access).

To do so, everything related to updating the ListView must be done using this.Invoke or this.Dispatcher.Invoke (in WPF).

EDIT
For example this thread here.

Sample code:

private delegate void MyDelegate(string s);

public void UpdateControl(Control targetControl, string text)
{
    if (targetControl.InvokeRequired)
    {
        // THIS IS STILL THE IN THE CONTEXT OF THE THREAD
        MyDelegate call = new MyDelegate(UpdateControl);
        targetControl.Invoke(call, new object[] { text });
    }
    else
    {
        // do control stuff
        // THIS IS IN THE CONTEXT OF THE UI THREAD
    }
}
like image 25
Thorsten Dittmar Avatar answered May 26 '26 13:05

Thorsten Dittmar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!