1

I have a form that needs to save unsaved data to an external DB via a web service call. When the user manually saves the data, the RunWorkerCompleted gets triggered, however not when I call the method via the FormClosing event.

The timer ticks after a few seconds after the user modifies a value in the form, and gets reset if the user modifies another value, the idea being that the 'toSend' list has more than one value and I can publish multiple changes at a time since web service calls are a lot of overhead.

    private void timer1_Tick(object sender, EventArgs e)
    {
        if (PublishBackgroundWorker.IsBusy)
            return;

        List<object> toSend = new List<object>();

        // Figure out what changed and add it to toSend ...
        toSend.Add(changedItems);

        if (toSend.Count >= 1)
            PublishBackgroundWorker.RunWorkerAsync(toSend);
    }

    private void PublishBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        if (((List<object>)(e.Argument)).Count > 0)
        {
            Service.SrvObjects[] ret = Svc.PublishItems(e.Argument));
        }
    }

    private void PublishBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { "Ready" });
    }

    private void IPTUserInterfaceForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        timer1.Enabled = false;
        timer1_Tick(this, new EventArgs());
        //Application.DoEvents();
        while (PublishBackgroundWorker.IsBusy)
            System.Threading.Thread.Sleep(100);
    }

Does this make sense? When the user changes a value, and closes the form before the timer goes off, I manually Tick hoping that the PublishBackgroundWorker.IsBusy is false at some point, and it never is because RunWorkerCompleted is never called.

Tizz
  • 820
  • 1
  • 15
  • 31
  • I do not see a reason why you would call `Invoke` from within `PublishBackgroundWorker_RunWorkerCompleted`. – Uwe Keim Apr 10 '13 at 19:12
  • Its just an update to the user that the publish to the server is complete. and since its on a seperate thread, I need a delegate to update the text on the form .... public delegate void UpdateStatusCallback(string text); private void UpdateStatus(string text) { StatusStripLabel.Text = text; System.Threading.Thread.Sleep(5); } – Tizz Apr 10 '13 at 19:13
  • 1
    First instinct: this sounds like you are missing an [Application.DoEvents](http://msdn.microsoft.com/en-us//library/system.windows.forms.application.doevents.aspx) call. See also [here](http://stackoverflow.com/questions/1115397/application-doevents-when-its-necessary-and-when-its-not) – Anders Gustafsson Apr 10 '13 at 19:14

1 Answers1

2

The problem is that your Backgroundworker is not busy just because you call the timerTick method. Therefore this code is never reached before the rest of the method runs through and the form gets closed and the application closes:

    while (PublishBackgroundWorker.IsBusy)
        System.Threading.Thread.Sleep(100);
Akku
  • 4,373
  • 4
  • 48
  • 67
  • But the background worker RunWorkerAsync is called in Timer_Tick, doesnt that trigger the worker to run, and therefore that thread is required to call RunWorkerCompleted after DoWork is completed? – Tizz Apr 10 '13 at 19:39
  • 1
    Try putting a `Thread.Sleep(100);` where in your code there's `//Application.DoEvents();`... I guess it's a race condition. – Akku Apr 10 '13 at 19:56
  • Got it ... if I put the `Application.DoEvents` in the while loop with the Sleep, it works. I get it. Thanks! – Tizz Apr 10 '13 at 20:05