1

My login window is highly unresponsive. I'm not really sure why is this. I'm using MVVM light as my MVVM framework. I handle the login button click through a RelayCommand, the command handler (I know it's bad practice to pass the whole PasswordBox as a parameter, it's breaking the MVVM pattern, but please focus on the question). The command handler is the following:

LoginButtonClicked = new GalaSoft.MvvmLight.Command.RelayCommand<System.Windows.Controls.PasswordBox>((pwdBox) => { if (string.IsNullOrWhiteSpace(this.Username) || string.IsNullOrWhiteSpace(pwdBox.Password)) { MessageBoxRequested("Neuspešno logovanje", "Molimo unesite i korsničko ime i lozinku"); return; } ProgressDialogRequested(); bool validated = false; try { validated = TotalSport.Framework.UserManagement.EmployeeStore.ValidateEmployee(this.Username, pwdBox.Password); } catch (System.Data.Entity.Core.ObjectNotFoundException) { MessageBoxRequested("Logovanje neuspešno", "Zaposleni sa tim imenom ne postoji!"); pwdBox.Password = string.Empty; return; } pwdBox.Password = string.Empty; System.Diagnostics.Debug.WriteLine(validated); if (validated) { Client.Views.MainWindow mw = new Client.Views.MainWindow(this.Username); mw.Show(); CloseRequested(); } else MessageBoxRequested("Logovanje neuspešno", "Pogrešna lozinka!"); }); 

ValidateEmployee hashed the supplied string and compares it to a value stored in the DB. I think it's the cause of my problems (it most likely is, since it's a slow hash - PBDKF2 - and a DB query in a single method), but I'm not sure why. It has nothing to do with the UI thread and the LoginWindow itself.

The relevant event handlers on the actual window:

((ViewModels.LoginViewModel)this.DataContext).MessageBoxRequested += (title, message) => { this.ShowMessageAsync(title, message); }; ((ViewModels.LoginViewModel)this.DataContext).ProgressDialogRequested += () => { this.ShowProgressAsync("Test", "test"); }; ((ViewModels.LoginViewModel)this.DataContext).CloseRequested += () => { this.Close(); }; 

Any input on this?

1 Answer 1

2

In your LoginButtonClicked handler, use the following code:

var username = this.Username; var password = pwdBox.Password; Task.Factory.StartNew(() => //This will run using a Thread-Pool thread which will not cause the UI to be unresponsive. { //Do expensive operations here, like data access //You cannot access the UI here bool valid = DoSomeExpensiveCallsToDetermineIfPasswordIsValid(username, password); return valid; }) .ContinueWith( t => //This will run on the UI thread { bool valid = t.Result; if (valid) { //Do some UI to indicate that the password is valid } else { //Do some UI to indicate that the password is not valid } }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, //Only run this if the first action did not throw an exception TaskScheduler.FromCurrentSynchronizationContext()); //Use the UI thread to run this action 

This code assumes that DoSomeExpensiveCallsToDetermineIfPasswordIsValid does not throw exceptions. Handle any exceptions inside such method.

This code runs the first action using a background thread, and then the second action in the UI thread.

The code that runs in the UI thread should be quick.

Sign up to request clarification or add additional context in comments.

4 Comments

Works like a charm! So to clarify some things I'm not sure about: 1. So the ViewModel DOES run on the UI thread? I thought it doesn't :( 2. Even if I make a method async, it still runs on the same thread? 3. Is this comparable to running a BackgroundWorker?
1) Yes, WPF ViewModels are invoked using the UI thread. 2) marking a method async does not mean it will run on a different thread, use Task.Factory.StartNew to run actions on a worker thread. 3) Yes it is comparable to BackgroundWorker.
Yeah, I totally didn't think about what ViewModels actually are. Obviously you couldn't bind anything if they were on a separate thread. About 3). What are the differences?
One of the main differences is that using the Task Parallel Library (TPL) (e.g. Tasks) is more flexible, you can use it to create a chain of continuations of tasks. It supports cancellation (i.e., allowing for canceling the task before it is done). TPL is a big component in .NET, it has a lot of features, I just mentioned a few. BackgroundWorker has a very specific use.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.