0

Here is my current code for the background worker, and I would like to get the label to say which file it is downloading.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    string[] FileArray = new string[] {
                "configs.pck", "interfaces.pck", "trees.pck", "elementskill.dll",
                "reportbugs\\zreportbugs.exe", "userdata\\server\\serverlist.txt",
                "userdata\\systemsettings.ini", "data\\aipolicy.data", "data\\domain.data",
                "data\\domain1.data", "data\\domain2.data", "data\\domain3.data",
                "data\\domain4.data", "data\\dyn_tasks.data", "data\\dynamicobjects.data", "data\\elements.data",
                "data\\forbidden_task.txt", "data\\gshop.data", "data\\gshop1.data", "data\\gshop2.data",
                "data\\hometowndata", "data\\path.data", "data\\task_npc.data", "data\\tasks.data",
                "data\\tasks.data1", "data\\tasks.data2", "data\\tasks.data3", "data\\tasks.data4",
                "data\\tasks.data5", "data\\tasks.data6", "data\\tasks.data7", "data\\tasks.data8",
                "data\\tasks.data9", "data\\tasks.data10", "data\\tasks.data11", "data\\tasks.data12",
                "data\\tasks.data13", "data\\tasks.data14", "data\\tasks.data15", "data\\tasks.data16",
                "data\\tasks.data17", "data\\tasks.data18", "data\\tasks.data19", "data\\tasks.data20",
                "data\\tasks.data21", "data\\tasks.data22", "data\\tasks.data23", "data\\tasks.data24",
                "data\\tasks.data25", "data\\tasks.data26", "data\\tasks.data27", "data\\tasks.data28",
                "data\\tasks.data29", "data\\tasks.data30", "data\\tasks.data31", "data\\tasks.data32",
                "data\\tasks.data33", "data\\tasks.data34", "data\\tasks.data35", "data\\tasks.data36",
                "data\\tasks.data37", "data\\tasks.data38", "data\\tasks.data39", "data\\tasks.data40",
                "data\\tasks.data41", "data\\tasks.data42", "data\\tasks.data43", "data\\tasks.data44",
                "data\\title_def.lua", "data\\title_def_u.lua", "data\\VIPAward.data"};

    foreach (string FileName in FileArray)
    {
        // The URL to download the file from
        string sUrlToReadFileFrom = "http://jd.paradise-gaming.org/Update/element/" + FileName;

        // The path to write the file to
        string sFilePathToWriteFileTo = Application.StartupPath + "\\element\\" + FileName;

        // First, we need to get the exact size (in bytes) of the file we are downloading
        Uri url = new Uri("http://jd.paradise-gaming.org/Update/element/" + FileName);
        System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
        System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
        response.Close();

        // Get last time file was modified
        FileSize = response.LastModified;

        string LatestWriteTime = Application.StartupPath + "\\element\\" + FileName; //Get most recent time file was saved.
        DateTime UserGshop = File.GetLastWriteTime(LatestWriteTime);
        UserFileTime = UserGshop;
        if (UserFileTime < FileSize)
        {
            // Gets the size of the file in bytes
            Int64 iSize = response.ContentLength;

            // Keeps track of the total bytes downloaded so we can update the progress bar
            Int64 iRunningByteTotal = 0;

            // Use the webclient object to download the file
            using (System.Net.WebClient client = new System.Net.WebClient())
            {
                // Open the file at the remote URL for reading
                using (System.IO.Stream streamRemote = client.OpenRead(new Uri(sUrlToReadFileFrom)))
                {
                    // Using the FileStream object, we can write the downloaded bytes to the file system
                    Directory.CreateDirectory(Path.GetDirectoryName(sFilePathToWriteFileTo));
                    using (Stream streamLocal = new FileStream(sFilePathToWriteFileTo, FileMode.Create))
                    {
                        // Loop the stream and get the file into the byte buffer
                        int iByteSize = 0;
                        byte[] byteBuffer = new byte[iSize];
                        while ((iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
                        {
                            // Write the bytes to the file system at the file path specified
                            streamLocal.Write(byteBuffer, 0, iByteSize);
                            iRunningByteTotal += iByteSize;

                            // Calculate the progress out of a base "100"
                            double dIndex = (double)(iRunningByteTotal);
                            double dTotal = (double)byteBuffer.Length;
                            double dProgressPercentage = (dIndex / dTotal);
                            int iProgressPercentage = (int)(dProgressPercentage * 100);

                            // Update the progress bar
                            backgroundWorker1.ReportProgress(iProgressPercentage);
                        }

                        // Clean up the file stream
                        streamLocal.Close();
                    }

                    // Close the connection to the remote server
                    streamRemote.Close();
                }
            }
        }
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pBarFileProgress.Value = e.ProgressPercentage;
    gbFileProgress.Text = String.Format("Current File Progress: {0} %", e.ProgressPercentage);
    lblCheckFile.Text = "Downloaing" + FileName;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    btnPlay.Enabled = true;
    btnFullCheck.Enabled = true;
    lblCheckFile.Text = "Download Complete!";
    gbFileProgress.Text = "No More Files to Check!";
    pBarFileProgress.Value = 0;
}

I have tried to put lblCheckFile.Text = "Downloaing" + FileName; on the backgroundworker1_DoWork and Progress_Change at different times, but the Progress_Change just shows "Downloading" and not the file name with it. When I put it under DoWork it errors and says Exception:Thrown: "Cross-thread operation not valid: Control 'lblCheckFile' accessed from a thread other than the thread it was created on."

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tomirons
  • 554
  • 2
  • 14
  • have a look at this: http://stackoverflow.com/questions/7428260/wp7-invalid-cross-thread-access-scheduledtaskagent/7428442#7428442 – thumbmunkeys Jan 19 '14 at 21:59
  • `ProgressChanged` is the correct place to do it. Are you sure the handler is actually attached/being called? If so, pass `FileName` to the handler in a class inheriting from `ProgressChangedEventArgs`, not just use it directly. – GSerg Jan 19 '14 at 22:01
  • @GSerg How exactly would I go about doing that? – tomirons Jan 19 '14 at 22:03
  • @Hulu8004 Sorry, not inherited. Just pass the file name as the second argument for `backgroundWorker1.ReportProgress` and retrieve it in `ProgressChanged` with `(string)e.UserState`. – GSerg Jan 19 '14 at 22:12
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Jan 19 '14 at 22:58

2 Answers2

3

The foreach loop in your DoWork event is creating the FileName string on-the-fly to iterate through the FileArray string array. It's not accessible outside that loop, so I'm not sure where the following FileName variable comes from in the ProgressChanged event:

lblCheckFile.Text = "Downloaing" + FileName;

Use the components built into the BackgroundWorker construct to report progress.

From your DoWork event:

...
// use the webclient object to download the file
using (System.Net.WebClient client = new System.Net.WebClient())
{
    ...
    // pass the filename as the second argument (change 0 to the correct %)
    backgroundWorker1.ReportProgress(0, FileName);
    ...

In your ProgressChanged event, you get the value you passed from e.UserState:

var fileName = Convert.ToString(e.UserState);

lblCheckFile.Text = string.Format("Downloading {0}", fileName);

To make this work, you'll have to remove your current call to:

backgroundWorker1.ReportProgress(iProgressPercentage);

And combine it with my call:

backgroundWorker1.ReportProgress(iProgressPercentage, FileName);

Otherwise, you'll briefly see your file name displayed in the label for a moment, but then the second call that passes iProgressPercentage will actually pass a null as the UserState, which will then get converted to an empty string and cause your file name to disappear from the label.

Grant Winney
  • 65,241
  • 13
  • 115
  • 165
  • That almost fixed the issue, but when it says downloading there is no "fileName" after it. its just blank. – tomirons Jan 19 '14 at 22:37
  • They both show the right file name, its just I can see the filename for a split second, but then it disappears until the next file is being downloaded, then it disappears. – tomirons Jan 19 '14 at 23:07
  • Thank you very much! just for a future reference, how many arguments can you pass in "1" BackgroundWorker? – tomirons Jan 19 '14 at 23:42
0

In Windows Forms (and most other GUI frameworks), you cannot do GUI operations from background threads. This is the meaning of the "cross-thread operation not valid" error you're receiving.

To do this, you need to do the label update on the main GUI thread by using the Invoke() / BeginInvoke() methods. If your background thread has a handle to the form, it can do this directly. Otherwise, a common design pattern is for the thread to raise an event which the form subscribes to; the form's event handler would then use Invoke/BeginInvoke to update the label on the GUI thread.

See also this discussion of BackgroundWorkers updating GUIs which has more info about this and other solutions.

Community
  • 1
  • 1
TypeIA
  • 16,916
  • 1
  • 38
  • 52
  • 1
    `ProgressChanged` is called on the UI thread, and the OP already has code in the right place. – GSerg Jan 19 '14 at 22:04