-1

I am working on a small project where I open a windows form from within another process to show a progress bar.

So my form code looks like this:

public partial class ProgressForm : Form
{
    int currentValue = 0;
    int pbMax;
    bool cancelled = false;
    public ProgressForm(int pbMax)
    {
        InitializeComponent();
        this.pbMax = pbMax;
        this.progressBar1.Maximum = pbMax;
    }

    public void updateProgressBar(int newValue)
    {
        currentValue = newValue;
        this.progressBar1.Value = newValue;
    }
    public bool getCancelledStatus()
    {
        return cancelled;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.cancelled = true;
    }
}

where button1 is my "cancel" button.

My plan was to enable a user to cancel the progress of another task by clicking the Cancel button.

The code in my other task is:

ProgressForm pf = new ProgressForm(MaxValue);
pf.Show();
bool cancelled = false;

 for (int i = 0; i<pbMax; i++)
 {
     if (cancelled == true)
         break;
       pf.updateProgressBar(i + 1);


/***** DO WORK HERE ******/

cancelled = pf.getCancelledStatus();

}

but I can't even click the cancel button. The whole form freezes up when showing the progress bar. Is there anything I can do about this? Can I use threading or something like that?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Woody
  • 1,677
  • 7
  • 26
  • 32
  • 6
    You should use BackgroundWorker, it has mechanism for reporting progress etc and runs the work in background. UI *must* be in the main thread. – Sami Kuhmonen Jun 29 '19 at 14:58
  • 1
    Did you try `Backgroundworker`? – Saeid Amini Jun 29 '19 at 15:00
  • The previous comment from @Sami isn't strictly true. There are ways to create additional threads to handle UI. But, doing so should be avoided if at all possible, and in 99.94% of all Winforms programs, it is entirely possible to run the entire UI in the main thread. If you have other logic that takes a long time and you need to avoid the UI being unresponsive, then it's the _other logic_ that belongs in a different thread, not some random piece of your UI. See marked duplicate for details on different ways to accomplish that. – Peter Duniho Jun 29 '19 at 18:01
  • I recommend to watch this video: https://www.youtube.com/watch?v=2moh18sh5p4 There is a second part, which explains very well how to cancel a background thread. Using threads depends a lot of what you really need. At least this works for me and gives you a nice strting point. – RudolfJan Jun 29 '19 at 18:35

1 Answers1

-4

The shortest answer to your question: Call DoEvents() method inside your FOR{} loop. When in a loop, Windows does not execute any other internal laces. With the DoEvents() call, your application will be able to attend the Button1_Click event, which will break the loop.

Your other task code will become something like this:

ProgressForm pf = new ProgressForm(MaxValue);
pf.Show();
bool cancelled = false;
for (int i = 0; i<pbMax; i++)
{
    if (cancelled == true)
        break;
    pf.updateProgressBar(i + 1);
    Application.DoEvents();
    cancelled = pf.getCancelledStatus();
}

I believe you should also add a Form Close() call when user clicks the Cancel Button on your Progress form, like below:

private void button1_Click(object sender, EventArgs e)
{
    this.cancelled = true;
    this.Close();
}

Doing this, when user clicks on Cancel button1 on ProgressForm, your For{} loop will be break, and ProgressForm will be closed.

This will do the trick!

As the long answer, however, I would recommend you to implement the same logic above using a Timer control, that could be more elegant and would not hang your application. But this is just a suggestion.

Good luck!

Overlord
  • 1
  • 1
  • 3
    Never call `DoEvents`. Use a [`BackgroundWorker` class](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker) instead. – Uwe Keim Jun 29 '19 at 15:37
  • As I stated in my answer, and I quote: "I would recommend you to implement the same logic using a Timer...". Of course you can use a new thread to do so, but if developer just need a GUI animation, these as faster ways to get same result without lots of code. – Overlord Jun 29 '19 at 16:12
  • 1
    Timer is a s bad as calling DoEvents(). BackgroundWorker (or Tasks) should be the way to go. – Uwe Keim Jun 29 '19 at 16:14
  • @Woody: Try DoEvents() and you'll see this solves your CURRENT problem. However, as you can see here, there are other better designs that you could implement to get a more elegant solution. Tasks would be one of them. BackgroundWorker would be another. – Overlord Jun 29 '19 at 16:26
  • 1
    Never, ever, ever call `DoEvents()` - it's only in the framework for backward compatibility with VB6. It can, and regularly does, cause re-entrancy bugs that are exceedingly difficult to remove. Use a timer, tasks, async/await, Rx, background worker, even explicitly created threads, but just don't use `DoEvents()`. – Enigmativity Jul 02 '19 at 00:11