3

Using .NET4, c#, I have a grid on a winform. When I hit a process button, I need to perform a timely operation with a few steps on each row and then update the row with a result. This way the user can see progress while the next row is processing. If the user hits cancel or closes the form, I need to finish the process for the current row and then break or close form. Based on my research, here is what I came up with. I'm left with 2 issues.

Here's my code:

delegate void OperateGridMethodDelegate();
delegate void UpdateProgressDelegate(int rowIndex, string msg);
bool isCancelled = false;


private void ButtonOne_Click(object sender, EventArgs e)
{
    isCancelled=false;
    OperateGridMethodDelegate delegate = new OperateGridMethodDelegate(LongProcess);`
    delegate.BeginInvoke(null,null);

}

private void ButtonTow_Click(object sender, EventArgs e)
{

    MyButtonOne.Enabled=false;
    isCancelled=true;

}

private void LongProcess()
{

    foreach (DataRow row in dataTableOne.Rows)
    {
        //Do Lengthy Process

        DisplayResult( rowIndex, "this is result of operation" );
        if(isCancelled)
        {
            MyButtonOne.Enabled=true;
            break;

        }

    }

}

private void DisplayResult(int rowIndex,string message)
{

    if(myGrid.InvokeRequired == false)
    {

        myGrid.Rows[rowIndex].value = message;

    }

    else
    {

        UpdateProgressDelegate delegate = new  UpdateProgressDelegate(DisplayResult);
        myGrid.BeginInvoke(delegate, new object[] { rowIndex, message });

    }

}

The 2 issues I'm left with are:

  1. When the form is closing, I want to wait for the LongProcess (that was called by my OperateGridMethodDelegate) to finish the row it's up to and then cancel (same way I did with ButtonTwo), but I can't get this to work.

  2. I notice a weird behavior when debugging and I'm not sure if this is a bug or correct, when a row is finished processing, the result does not get set. Instead, the next row is processed, and then the previous row result gets displayed. The second row result is displayed when the third row finishes etc. Then the last 2 row results get displayed basically at the same time.

Am I approaching this correctly?

Mike Turner
  • 471
  • 1
  • 7
  • 22
  • 3
    Don't use a delegate's BeginInvoke() method to implement threading, a *bool* is not a synchronization primitive. There are better ways to do this, how to correctly handle this for a BackgroundWorker is covered in [this Q+A](http://stackoverflow.com/a/1732361/17034). – Hans Passant Jun 22 '16 at 18:30

1 Answers1

3

If you've launched some task in a thread, to wait for it to finish before proceeding you can do:

yourThread.Join();

This will block until the thread exits. You may want to use something like this in your Form_Closing event

Alternatively, if you want the thread to automatically terminate if the application is terminated, you need to set the background property of the thread:

yourThread.IsBackground = true;

This means it is a background thread of your main (UI) thread, so it will end with the main thread.

Edit:

Using your own thread:

private Thread yourThread = new Thread(() = > LongProcess());
yourThread.IsBackground = true;
yourThread.Start();  // This will begin your 'LongProcess' function.

Now if you want to block somewhere and wait for this to complete, you can do what I mentioned first: yourThread.Join();

Also note that if you set IsBackground = false and someone closes the application, that thread will not exit immediately, it will continue until it is complete despite the UI window being closed.

pay
  • 366
  • 3
  • 18
  • what is yourThread? where do i get this? Im not following exactly – Mike Turner Jun 22 '16 at 18:12
  • I've added more to the answer. If you want to control the thread, you should use your own `Thread` object – pay Jun 22 '16 at 18:14
  • Are you trying to say that instead of my code - private void ButtonOne_Click(object sender, EventArgs e) { isCancelled=false; OperateGridMethodDelegate delegate = new OperateGridMethodDelegate(LongProcess);` delegate.BeginInvoke(null,null); }, I should do this? private void ButtonOne_Click(object sender, EventArgs e) { isCancelled=false; private Thread yourThread = new Thread(() = > LongProcess()); yourThread.IsBackground = true; yourThread.Start(); } – Mike Turner Jun 22 '16 at 18:18
  • I wouldn't do it the way you're doing it in general, but given the control you want over your long running task, using your own `Thread` is definitely advisable. You would declare the thread probably where you've declared your `delegate void`'s, and `.Start()` the thread in the `ButtonOne_Click` event – pay Jun 22 '16 at 18:22
  • Would you mind showing me how you would advise doing it? I'm not set on doing it this way only? – Mike Turner Jun 22 '16 at 18:26
  • I've described in exact detail in this answer and the few comments I've made how I would do it. In fact the code I have here is basically all the code you would need. – pay Jun 22 '16 at 18:27
  • Sorry. I don't see what you mean. I started trying to implement your code, even the first line creating the thread like you showed gives an error – Mike Turner Jun 22 '16 at 18:35
  • Assuming you're using Visual Studio, you have to ensure you have to right namespaces included. To access `Thread`, you would need `using System.Windows.Threading`, but if you hover over the error on `Thread`, it should tell you this. – pay Jun 22 '16 at 18:36
  • All my example is doing is creating your own thread that you can manage yourself. The 3 lines I have on the bottom are creating the `Thread` object. – pay Jun 22 '16 at 18:37
  • Thanks for the details.. .down to the import statment :). What I'm trying to figure out is - 1. do you mean to use thread.start instead of operateGridMethodDelegate.BeginInvoke? 2. To still use my same code in DisplayResult method? – Mike Turner Jun 22 '16 at 19:04
  • I'm not entirely sure what you're doing in that method. I would simplify it though, whatever it is. Are you just updating a UI element? – pay Jun 22 '16 at 20:11
  • I am updating a UI element, but according to .NET rules, you must only update a UI element by the thread that created the control – Mike Turner Jun 22 '16 at 21:02
  • Yes, that is what the `Dispatcher` class is for. – pay Jun 22 '16 at 21:22
  • Where do you use Dispatcher? I dont see that – Mike Turner Jun 22 '16 at 23:39