0

I am creating a program that can read lots of data from a file when it starts up.

I tried a progress bar that will display the progress of the load hoping that it will stop the not responding but instead it froze halfway through and updating a label on the screen to fix the problem.

//This will be 'private void Form1_Shown(object sender, EventArgs e)'
private void loadingBarToolStripMenuItem_Click(object sender, EventArgs e)
{
    progressBar1.Show();
    for (int i = 0; i < 100; i++)
    {
        progressBar1.Value = i;
        System.Threading.Thread.Sleep(50);
        //StartRespondingAgain();
    }
    progressBar1.Hide();
}
  • If you have a long load of work move it to a backgroundworker! From there you can update the progessbar. Lots of examples around.. - Whatever the sleep was supposed to do, you do not want it! ((Nor btw any Application.DoEvents !!)) – TaW Oct 19 '19 at 10:17
  • If you don't want to use multithread, you can add `progressBar1.Refresh()` and `Application.DoEvents()` in the loop before the `Sleep()` that you can remove. –  Oct 19 '19 at 10:18
  • Actually this only seems to work because there is no actual load. – TaW Oct 19 '19 at 10:40
  • @TaW I had the same thing happen when debugging the program but I had the issue when running the program from file explorer. I think that when debugging from Visual Studio the not responding feature is disabled. – Doshorte Dovencio Oct 19 '19 at 11:02

1 Answers1

0

This issue is not the loading bar, but that you are running a long lasting operation on the GUI Thread.

Events are suppossed to be called, finish their work and return ASAP. Only one piece of code can run at the same time, and while this event runs, no other event - indeed not even the drawing of the changes - can be executed.

You need to add some form of Multitasking. This operation will not benefit from a lot of Threads/tasks (like one per file), but at least the long running loop can be moved into a seperate task. BackgroundWorkers, Threads and Async/Await are just 3 approaches. I personally consider the BackgroundWorker to be good - but slightly dated - "training Wheels" to learning Multitasking and in particular Multithreading. I even got some example code for it:

#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
    if (!bgwPrim.IsBusy)
    {
        //Prepare ProgressBar and Textbox
        int temp = (int)nudPrim.Value;
        pgbPrim.Maximum = temp;
        tbPrim.Text = "";

        //Start processing
        bgwPrim.RunWorkerAsync(temp);
    }
}

private void btnPrimCancel_Click(object sender, EventArgs e)
{
    if (bgwPrim.IsBusy)
    {
        bgwPrim.CancelAsync();
    }
}

private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
    int highestToCheck = (int)e.Argument;
    //Get a reference to the BackgroundWorker running this code
    //for Progress Updates and Cancelation checking
    BackgroundWorker thisWorker = (BackgroundWorker)sender;

    //Create the list that stores the results and is returned by DoWork
    List<int> Primes = new List<int>();


    //Check all uneven numbers between 1 and whatever the user choose as upper limit
    for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
    {
        //Report progress
        thisWorker.ReportProgress(PrimeCandidate);
        bool isNoPrime = false;

        //Check if the Cancelation was requested during the last loop
        if (thisWorker.CancellationPending)
        {
            //Tell the Backgroundworker you are canceling and exit the for-loop
            e.Cancel = true;
            break;
        }

        //Determin if this is a Prime Number
        for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
        {
            if (PrimeCandidate % j == 0)
                isNoPrime = true;
        }

        if (!isNoPrime)
            Primes.Add(PrimeCandidate);
    }

    //Tell the progress bar you are finished
    thisWorker.ReportProgress(highestToCheck);

    //Save Return Value
    e.Result = Primes.ToArray();
}

private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pgbPrim.Value = e.ProgressPercentage;
}

private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    pgbPrim.Value = pgbPrim.Maximum;
    this.Refresh();

    if (!e.Cancelled && e.Error == null)
    {
        //Show the Result
        int[] Primes = (int[])e.Result;

        StringBuilder sbOutput = new StringBuilder();

        foreach (int Prim in Primes)
        {
            sbOutput.Append(Prim.ToString() + Environment.NewLine);
        }

        tbPrim.Text = sbOutput.ToString();
    }
    else 
    {
        tbPrim.Text = "Operation canceled by user or Exception";
    }
}
#endregion

But that is an area where you can pick your poison.

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • Thanks for the quick answer however I think that multithreading would not be the best option in this case as I do not want the user to change any data while it is being read. This is especially true when the data is being saved. – Doshorte Dovencio Oct 19 '19 at 10:35
  • @DoshorteDovencio Then do not give the user the **option** to change the data befoire it is all read, by not showing anything or enabling edit before you are finished. You will **need** to do **some** form of Multitasking, and if it is DoEvents(). There is only this one solution for the problem, but you got a lot of ways to do it. – Christopher Oct 20 '19 at 11:05
  • is there also a way of locking the application so that the user can't change anything like `System.IO.lockbuttons()`? – Doshorte Dovencio Oct 21 '19 at 05:29
  • The simplest way is to not show anything on the UI, untill all data is ready. And clean it when you start to retreive a new set of data. Otherwise, flipping the enabeled state on the container is a quick but dirty way. But that will also disable stuff like scrollbars so it might not be the right idea. – Christopher Oct 21 '19 at 11:42