views:

57

answers:

1

Hi there

I have a problem with my BackgroundWorker placed in a UserControl. My WPF application has a navigation on the left and each entry loads its own UserControl where the user can generate a PDF file.

Because the creation of the PDF takes some time I've implemented a BackgroundWorker which does the job and additionally I disable some buttons and show a progressbar. In the RunWorkerCompleted event I reset the state of the buttons and hide the progressbar.

All of this is working very well, despite one scenario: While the PDF creation is running the user can switch between the UserControls and if he returns to the control where he startet the job, the control should display the progressbar and the buttons as disabled. To achieve this I added a variable (isProcessing) to the UserControl.

A the constructor of the control I have this:

        // Check if a current process is running, if so: handle button/progressbar visibility
        if (_isProcessing)
        {
            _stkProgressBar.Visibility = Visibility.Visible;
            progressBar1.IsIndeterminate = true;

            // Disabling the buttons here is just working with a hack in
            // the "Button_IsEnabledChanged" event.
            btnDaten.IsEnabled = false;
            btnBericht.IsEnabled = false;
            this.Cursor = Cursors.Wait;
        }
        else
        {
            _stkProgressBar.Visibility = Visibility.Hidden;
        }

Enabling/Disabling the buttons there is just working because of this dirty hack:

    private void btnDaten_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        //HACK: We want to disable the report buttons if a report execution is running.
        //      Disabling the buttons in the Load Event of the control isn't working
        if (_isProcessing && (bool)e.NewValue)
        {
            btnDaten.IsEnabled = false;
            btnBericht.IsEnabled = false;
        }
    }

Now if a job is running and the user switches between the control, the state of the processing control is fine. But if the job is over and the PDF ready, the buttons can't get enabled and the progressbar also stays visible. The code is placed in the RunWorkerCompleted event:

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // This is not working if the user switches between the controls
        _isProcessing = false;
        this.Cursor = Cursors.Arrow;
        _stkProgressBar.Visibility = Visibility.Hidden;
        btnDaten.IsEnabled = true;
        btnBericht.IsEnabled = true;
    }

I debugged it and saw that the buttons get the right input and therefore should be enabled, but nothing happens. If the user stays in the control where he startet the job, the state of the buttons and the progressbar is resetted correctly.

+2  A: 

Yes, the switching between user controls is the problem here. When you switch away and switch back, you create a new instance of the control. Which creates a new instance of the BackgroundWorker. Which has a RunWorkerCompleted event handler that is not associated with the BGW that is actually running.

There's another bug in your code, this should crash your program with an ObjectDisposedException when the original BGW instance finishes the job and sets the (now invisible) control properties. You are forgetting to call Dispose() on the user controls when you switch between them. Not quite sure how that's done in WPF but in winforms that is an unpluggable leak.

You are going to have to this differently as long as you want to support switching. The BGW instance needs to be separate from the user control instance so it can survive the switch. Fairly painful, you'll have to wire and unwire the events as the control gets created and disposed, you definitely need to override the Dispose() method and not forget to call it. Making the BGW static is defensible if you only allow the job it does to run one-at-a-time. Which should be normal. Stacking the user controls so you only ever create them once is a Q&D fix.

Hans Passant
Thank you for your answer. It sounds promising, I'll try this approach and report my results here.
Jottiza
I tried your approach and I was successful, thank you very much!
Jottiza