views:

180

answers:

2

I already searched the forum but no asked question fits to my problem I think ...

I use a BackgroundWorker to handle a very time consuming operation. After this operation finishes a button should be set to enabled so that the user can perform another operation.

I'm using WPF and I'm following the MVVM pattern, so I have no direct access to this button. I have to call a method in the BackgroundWorker_RunWorkerCompleted event handler which sets a Property, representing the buttons enabled-state, to true.

That all works fine except one thing: The button is only redrawn after I e.g. click into the window (or maximize the window, ...). Thats very annoying and took me the whole day to get rid of this behaviour but I can't find a solution ...

The BackgroundWorker_RunWorkerCompleted event handler looks like this:

    void fileLoadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        SetButtonToEnabled(); }

Has anyone any idea how to fix this issue?

Edit:

The button is bound to a command:

<Button Name="btnLoadTargetFile" Command="{Binding Path=LoadTargetCommand}" .../>

This command is a RelayCommand as Smith stated in his blog (http://msdn.microsoft.com/en-us/magazine/dd419663.aspx) and looks like this:

public RelayCommand LoadTargetCommand
    {
        get
        {
            if (loadTargetCommand == null)
            {
                loadTargetCommand = new RelayCommand(param => this.OnRequestLoadFile(BusinessLogic.CustomTypes.TreeType.Target), param => this.CanLoadTarget);
            }
            return loadTargetCommand;
        }
        set { loadTargetCommand = value; }
    }

this.CanLoadTarget is set to true by the SetButtonToEnabled(); method

Edit 2:

the following code 'works':

fileLoadBackgroundWorker.RunWorkerAsync(argumentList);
while(fileLoadBackgroundWorker.IsBusy)
  System.Windows.Forms.Application.DoEvents();
SetButtonToEnabled();

but that is some sort of really dangerous and ugly code ...

A: 

Your problem probably has nothing to do with the BackgroundWorker. If you call the SetButtonToEnabled method directly from the main thread you will probably see the same behavior. Replace the call to RunWorkerAsync with one to SetButtonToEnabled to test this.

A couple of the commenters suggested that you look at your INotifyPropertyChanged implementation this sounds like good advice.

Scott Munro
thanks for your answer! If i call SetButtonToEnabled in the main thread everything works fine ...
lakai
Can you confirm that the RunWorkerCompleted event is being raised? Does a breakpoint that you set there get hit?
Scott Munro
yes it does, breakpoint gets hit
lakai
The only other thing that I can think of is to confirm that the event handler is being executed on the main thread.
Scott Munro
maybe a stupid question but how to confirm that? ;-)
lakai
In SetButtonToEnabled, check the Button's InvokeRequired Property to see if its true
SwDevMan81
Actually for WPF, looks like you check the Dispatcher, so something like this: btnRefresh.Dispatcher.CheckAccess(). See this post: http://www.diranieh.com/NET_WPF/Threading.htm
SwDevMan81
I think that the Debug Location Toolbar should show you which thread that you are on. Access it via View | Toolbars | Debug Location
Scott Munro
+1  A: 

Althought it doesnt explain your problem, you could try to add

System.Windows.Forms.Application.DoEvents();

or

Dispatcher.Invoke(new Action(delegate { }), DispatcherPriority.Background);

to the end of your SetButtonToEnabled function call (after you set the button Enabled to true). It looks like the backgroundworker is calling the Complete event, but the GUI is not refreshing.

Here is a complete example using the Backgroundworker in WPF. You might want to take a look at it and make sure you stuff looks the same.

You could also take a look at Asynchronous Threading with Background Worker Object. This is a good example of using the Dispatcher and the Backgroundworker

The WPF Threading Model is another great source to check out.

SwDevMan81
thanks for your comment! my code looks like the example except that I have no direct access to the buttons because of the MVVM pattern I use
lakai
Could you post the code for SetButtonToEnabled. I dont think its a problem with backgroundworker, since you mentioned that Complete does get called. Its probably something you are doing on the main thread. What are you doing while the process is running in the background?
SwDevMan81
You might want to check out InvalidateRequerySuggested: http://msdn.microsoft.com/en-us/library/system.windows.input.commandmanager.invalidaterequerysuggested.aspx Try adding this to the end of SetButtonToEnabled
SwDevMan81
This post might also help: http://stackoverflow.com/questions/783104/refresh-wpf-command
SwDevMan81
Thanks a lot for the links, the call of CommandManager.InvalidateRequerySuggested() fixed it!
lakai