tags:

views:

233

answers:

3

I've created a busy indicator - basically an animation of a logo spinning. I've added it to a login window and bound the Visibility property to my viewmodel's BusyIndicatorVisibility property.

When I click login, I want the spinner to appear whilst the login happens (it calls a web service to determine whether the login credentials are correct). However, when I set the visibility to visible, then continue with the login, the spinner doesn't appear until the login is complete. In Winforms old fashioned coding I would have added an Application.DoEvents. How can I make the spinner appear in WPF in an MVVM application?

The code is:

        private bool Login()
        {
            BusyIndicatorVisibility = Visibility.Visible;
            var result = false;
            var status = GetConnectionGenerator().Connect(_model);
            if (status == ConnectionStatus.Successful)
            {
                result = true;
            }
            else if (status == ConnectionStatus.LoginFailure)
            {
                ShowError("Login Failed");
                Password = "";
            }
            else
            {
                ShowError("Unknown User");
            }
            BusyIndicatorVisibility = Visibility.Collapsed;
            return result;
        }
+6  A: 

You have to make your login async. You can use the BackgroundWorker to do this. Something like:

BusyIndicatorVisibility = Visibility.Visible; 
// Disable here also your UI to not allow the user to do things that are not allowed during login-validation
BackgroundWorker bgWorker = new BackgroundWorker() ;
bgWorker.DoWork += (s, e) => {
    e.Result=Login(); // Do the login. As an example, I return the login-validation-result over e.Result.
};
bgWorker.RunWorkerCompleted += (s, e) => {
   BusyIndicatorVisibility = Visibility.Collapsed;  
   // Enable here the UI
   // You can get the login-result via the e.Result. Make sure to check also the e.Error for errors that happended during the login-operation
};
bgWorker.RunWorkerAsync();

Only for completness: There is the possibility to give the UI the time to refresh before the login takes place. This is done over the dispatcher. However this is a hack and IMO never should be used. But if you're interested in this, you can search StackOverflow for wpf doevents.

HCL
Crap. I hoped that wasn't the answer. I have inherited a huge application with UI stuff happening all over the place and I have one day to add this busy indicator to about 50 screens. There's no easier way?
Unhappy
The other problem is that I tried that on the login screen and it breaks the tests :(
Unhappy
Maybe something like this: In the Login()-method, open a modal window (window.ShowDialog) and do the login there in. Close the window after the login has been completed. So the app is blocked until the login has been done. The window itselfs shows that the app is busy. Within the window, I would use BackgroundWorker.
HCL
I tried to DoEvents stuff. Weirdly, it works fine when I click the Login button. However, if I press Enter (the Login button is the default button), although the exact same code runs, the doevents stuff doesn't work and the busy indicator doesn't show.
Unhappy
A: 

Does your login code run on the UI thread? That might block databinding updates.

Botz3000
Yes unfortunately.
Unhappy
A: 

You can try to run busy indicar in a separate thread as this article explains: Creating a Busy Indicator in a separate thread in WPF

Or try running the new BusyIndicator from the Extended WPF Toolkit

But I'm pretty sure that you will be out of luck if you don't place the logic in the background thread.

Eduardo Molteni