Click here to Skip to main content
15,885,278 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I created a dll named HttpDownloader. It has three events. ProgressChanged,DownloadCompleted and DownloadCancelled. When I use the ProgressChanged event to write the progress to a listview in Form.cs it throws an InvalidOperationException which "cross thread operations not allowed" is written in its text message.

How can I fix this without invoking control in Form.cs . I copied my code parts that I think it's necessary to be understanded below.

C#
void Download(int startRange, bool overWrite)
{
//some code lines....
while ((bytesRead = str.Read(buffer, 0, buffer.Length)) > 0)
            {
                if (state == DownloadState.Cancelled | state == DownloadState.Paused) break;
                state = DownloadState.Downloading;
                file.Write(buffer, 0, bytesRead);
                file.Flush();
                bytesReceived += bytesRead;
                speedBytes += bytesRead;
                this.Progress = progress = (int)(bytesReceived * 100.0 / contentLength);
                speed = (int)(speedBytes / 1.0 / stpWatch.Elapsed.TotalSeconds);

            }
//some code lines....
}

public int Progress
        {
            get { return progress; }
            private set
            {
                progress = value;
                if (DownloadProgressChanged != null)
                    DownloadProgressChanged.Invoke(this, new DownloadProgressEventArgs(progress, speed));
            }
        }
public async void StartDownloadAsync()
        {
            if (state != DownloadState.Started & state != DownloadState.Completed & state != DownloadState.Cancelled)
                return;

            state = DownloadState.Started;
            await Task.Run(() =>
            {
                Download(0, false);
            });

        }
void downloader_QueueProgressChanged(object sender, EventArgs e)
        {
            listViewDwlds.Items[0].Text = downloader.CurrentProgress + "%";
        }
{
Posted
Comments
Richard Deeming 30-Oct-15 14:27pm    
You solve the problem by using the Invoke method in the event handler.

That's how every other event handler solves it; what's so special about yours that you can't use it?
Amt-Coder 30-Oct-15 14:37pm    
i think it is not professional. WebClient class has a DownloadProgressChanged event and DownloadFileTaskAsync method like my dll. But in its progress event it's not necessary to use invoke. I want to fix the problem like this class but i dont know how.

It does not matter if event is custom or not. You cannot directly use methods and properties of the elements of the UI from a non-UI thread. Instead, you can use UI thread invocation mechanism. You need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
.NET event on main thread[^],
Control.Invoke() vs. Control.BeginInvoke(),
Problem with Treeview Scanner And MD5.

See also more references on threading:
.NET event on main thread,
How to get a keydown event to operate on a different thread in vb.net,
Control events not firing after enable disable + multithreading.

—SA
 
Share this answer
 
Comments
Amt-Coder 9-Nov-15 19:35pm    
Now, I have a clean solution for this problem and have added below. Thanks for all.
I have just found a better solution for this problem using AsyncOperationManager class. A code sample is below.


C#
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        Counter myCounter = new Counter(1000);
        myCounter.IndexValueChanged += myCounter_IndexValueChanged;
        myCounter.StartCountAsync();
    }
    void myCounter_IndexValueChanged(object sender, IndexValueChangedEventArgs e)
    {
        textBox1.Text = e.Index.ToString();
    }
}
class Counter
{
    public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
    public event IndexValueChangedEventHandler IndexValueChanged;

    int _maxNumber;
    int _index;

    public Counter(int maxNumber)
    {
        _maxNumber = maxNumber;
    }

    public async void StartCountAsync()
    {
        //We create an instance of AsyncOperation class
        AsyncOperation asyncCountOperation = AsyncOperationManager.CreateOperation(null);

        await Task.Run(() =>
        {
            for (int i = 0; i < _maxNumber; i++)
            {
                _index = i;
                //We use post method with method will be invoked
                asyncCountOperation.Post(new SendOrPostCallback(delegate
                    {
                        if (IndexValueChanged != null)
                            IndexValueChanged(this,
                                           new IndexValueChangedEventArgs(_index));
                    }), null);
                /*If we raise event without Post method like below it throws an error because of cross thread calls
                 * if (IndexValueChanged != null)
                            IndexValueChanged(this,
                                           new IndexValueChangedEventArgs(_index));
                */
            }
        });
    }
}
class IndexValueChangedEventArgs
{
    int indexNum;
    public IndexValueChangedEventArgs(int index)
    {
        indexNum = index;
    }
    public int Index
    {
        get { return indexNum; }
    }
}
 
Share this answer
 
v4

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