4

I'm working on an application that should read the data from Mifare smart card. I have to create a form will check the Mifare reader periodically and when the card is in range, read its serial number and send it to the parent form. I managed to get the background worker to read the serial number, but I can't close the form from it due to cross thread calling error it would cause. Is there a way to monitor the work that backGroundWorker does, and when it successfully reads the card ID, to stop it and close the child form? This is the code I'm using in the DoWork method:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    while (!worker.CancellationPending)
    {
        MifareReader.CommPort = 4;
        MifareReader.PortOpen = true;
        MifareReader.mfAutoMode(true);               
        MifareReader.mfRequest();                
        if (CardID == "0" || CardID == string.Empty)
        {
            MifareReader.mfRequest();
            CardID = MifareReader.mfAnticollision().ToString();
            MifareReader.mfHalt();
        }
        else if (CardID != "0" && CardID != string.Empty)
        {
            MessageBox.Show(ObrnutiID);
            worker.CancelAsync();                    
        }
        MifareCitac.mfHalt();
    }
}

This code does it's job, but I have to manually close the form. Is there a way to check if the CardID variable changes it's value in the main thread and if it does, close the form. I tried to solve this problem by using a timer, but when I do that, the timer blocks the main form thread, and I can't close it manually (which of course I have to be able). Can you please suggest a way to solve this problem?

John Willemse
  • 6,608
  • 7
  • 31
  • 45
NDraskovic
  • 706
  • 2
  • 22
  • 50
  • 2
    Calling CancelAsync() inside the DoWork event handler makes little sense. Just use *break* to break out of the while() loop. Closing the window is simple in the RunworkerCompleted event handler. The much harder problem to solve is what to do when the user closes the window before the worker is done. Don't ignore that. Check [this answer](http://stackoverflow.com/a/1732361/17034) – Hans Passant May 27 '13 at 09:53

5 Answers5

4

You can use the BackgroundWorker.RunWorkerCompleted event to monitor when the BackgroundWorker is done.

Occurs when the background operation has completed, has been canceled, or has raised an exception.

From there, you could close the form programmatically.

John Willemse
  • 6,608
  • 7
  • 31
  • 45
  • Ok, but how do I "explain" to the BackgroundWorker that he is finished (this is the first time I work with BackgroundWorker, so please be patient with me). Logically, his task is finished when CardID changes from "0" or string.Empty into some number. How would I tell the BackgroundWorker that he should complete it's work when this occurs? – NDraskovic May 27 '13 at 10:02
  • That is defined by you in the `DoWork` method. The `while` statement you are using in the OP defines a point where the loop will exit and the method will return. Right now you are waiting for the process to be canceled. When the DoWork method ends, the BackgroundWorker considers its work completed and the RunWorkerCompleted event will be fired. – John Willemse May 27 '13 at 10:07
3

You can write your BackgroundWorker so that it returns when it has finished its work.

Then in your main form, subscribe to the BackgroundWorker.RunWorkerCompleted and respond appropriately, presumably by just closing the form.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1

Add RunWorkerCompleted event from your backgroundWorker:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    this.Close() // Closes the form.
}
yonan2236
  • 13,371
  • 33
  • 95
  • 141
1

You can avoid cross-thread calling error by checking InvokedRequired and try to BeginInvoke using delegates.

user1552814
  • 31
  • 1
  • 5
1

As others mentioned, here is how to implement the BackgroundWorker.RunWorkerCompleted event:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    bool found = false;
            var worker = sender as BackgroundWorker;
    while (!worker.CancellationPending && !found)
    {
        MifareReader.CommPort = 4;
        MifareReader.PortOpen = true;
        MifareReader.mfAutoMode(true);               
        MifareReader.mfRequest();                
        if (CardID == "0" || CardID == string.Empty)
        {
            MifareReader.mfRequest();
            CardID = MifareReader.mfAnticollision().ToString();
            MifareReader.mfHalt();
        }
        else
        {
            e.Result = ObrnutiID;
            found = true;
            MifareCitac.mfHalt();
        }
    }
    if (worker.CancellationPending)
    {
        e.Cancel = true;
    }
}

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            // The user canceled the operation.
            MessageBox.Show("Operation was canceled");
        }
        else if (e.Error != null)
        {
            // There was an error during the operation. 
            string msg = String.Format("An error occurred: {0}", e.Error.Message);
            MessageBox.Show(msg);
        }
        else
        {
            // The operation completed normally. 
            string msg = String.Format("Result = {0}", e.Result);
            MessageBox.Show(msg);
        }
        this.Close() // Closes the form.
    }
Ahmed KRAIEM
  • 10,267
  • 4
  • 30
  • 33