Search code examples
c#wpfdispatchersta

Parallel running Progress Bar "Indeterminate" on Login in WPF


Can someone help me on following problem ?

I want to show a "Progress Bar" that is "Indeterminate = TRUE" while the user is connecting to SSH server. If the the connection to the server is successfull, then the MainWindow with progressbar should be closed and a new Window will be opened. The progressbar is placed on a other Window named "LoginState".

Here a little codexample how I tried it:

Code from Login Methode:

private async void Login(object sender, RoutedEventArgs e)
{
  loginstate.Show();
  dispatcherlogin = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DelegateLogin(ProcessLogin));
  await dispatcherlogin.Task;
  
  if(ProofIfUserActive() == true)
  {
     loginstate.Close();
     mainoverview.Show();
     Close();
  }

private void ProcessLogin()
{
   // SSH and MySQL Connection
}

My problem:

If the Button is pressed the Methode "Login" will be triggered. In this moment the progress bar appeares and the connection to the ssh server start. The problem is, that I can't see any progress in the progress bar while it's opened, and after the succesfull connection, the main window with the progressbar will be closed and the other window will be opened.

I tried it also with the Dsipatcher.Invoke Method, but without success. Is that even possible what I want, because how I unterstood it, the wpf application is runnung in UI "STA Thread". Multithread is not possible, because I got immedtiatly a Exception that only STA threads are allowed.

Thanks developers for the help ;)


Solution

  • You should not put the blocking code on the Dispatcher. Dispatcher is keeping the UI thread alive by implementing a message queue. If you let the Dispatcher execute your long running operation, you are blocking the message queue that holds jobs that execute UI related operations like input events and rendering - the UI freezes.

    Instead execute the long running CPU bound operations on a background thread which you can await.

    Please note: the best solution is always to use an existing async API of your server (or the library used to interact with your server).
    For example the HttpClient exposes a naturally async API.
    IO operations like connecting to a database are not CPU bound and should generally not be executed on a background thread using Task.Run!

    private async void Login(object sender, RoutedEventArgs e)
    {
      loginstate.Show();
    
      await Task.Run(ProcessLogin);
    
      loginstate.Close();
      
      ...
    }
    

    The cleaner solution is, in case your login service is event driven, to use TaskCompletionSource to implement asynchronous behavior.