lavoyech
lavoyech

Reputation: 11

WPF UI not refreshing

I have a problem to refresh my UI in a wpf / mvvm project.

When I log out there is a action on UI to display "waiting" object and then do a long process.

All codes I present are not complete

private void btnPower_Click(object sender, RoutedEventArgs e)
{
      // IHM changes
      btnRightMenuShow.IsEnabled = false;
      btnRightMenuShow.Visibility = Visibility.Hidden;
      StationWindowViewModel stnWindowVM = (StationWindowViewModel)DataContext;
      stnWindowVM.ProgressMode = LoadingProcessViewModel.ProgressMode.Sending;

      // PRocess
      if (stnWindowVM.PowerOffCmd.CanExecute(null))
      {
          stnWindowVM.PowerOffCmd.Execute(null);
      }
}

Like this the ihm is like frozen during the process and never refresh() before the application ended.

I first try to use a background worker like

private void btnPower_Click(object sender, RoutedEventArgs e)
{
      // IHM changes
      btnRightMenuShow.IsEnabled = false;
      btnRightMenuShow.Visibility = Visibility.Hidden;
      StationWindowViewModel stnWindowVM = (StationWindowViewModel)DataContext;
      stnWindowVM.ProgressMode = LoadingProcessViewModel.ProgressMode.Sending;

      // PRocess
      powerOffWorker.RunWorkerAsync(stnWindowVM);
}

private void powerOffWorker_DoWork(object Sender, System.ComponentModel.DoWorkEventArgs e)
{
      if (stnWindowVM.PowerOffCmd.CanExecute(null))
      {
          stnWindowVM.PowerOffCmd.Execute(null);
      }
}

In this case the IHM is refreshed but i have threading errors with some code trying to access object created by parent thread on main process (The calling thread cannot access this object because a different thread owns it)

So i try to use a Dispatcher like

private void btnPower_Click(object sender, RoutedEventArgs e)
{
      // IHM changes
      btnRightMenuShow.IsEnabled = false;
      btnRightMenuShow.Visibility = Visibility.Hidden;
      StationWindowViewModel stnWindowVM = (StationWindowViewModel)DataContext;
      stnWindowVM.ProgressMode = LoadingProcessViewModel.ProgressMode.Sending;

      // PRocess
      this.Dispatcher.Invoke(new Action(() =>
      {
          if (stnWindowVM.PowerOffCmd.CanExecute(null))
          {
              stnWindowVM.PowerOffCmd.Execute(null);
          }
      }));
}

I also try dispatcher inside backgrounworker but with those dispatcher i don't have anymore problem with calling thread but the UI is not updating.

EDIT

This is my test for async/await implementation

private void btnPower_Click(object sender, RoutedEventArgs e)
{
  // IHM changes
  btnRightMenuShow.IsEnabled = false;
  btnRightMenuShow.Visibility = Visibility.Hidden;
  StationWindowViewModel stnWindowVM = (StationWindowViewModel)DataContext;
  stnWindowVM.ProgressMode = LoadingProcessViewModel.ProgressMode.Sending;

  // PRocess
  PowerOffAsync(stnWindowVM);
}

private async void PowerOffAsync(StationWindowViewModel stnWindowVM)
{
    await Task.Run(() =>
    {
        if (stnWindowVM.PowerOffCmd.CanExecute(null))
         {
            stnWindowVM.PowerOffCmd.Execute(null);
        }
    });
}

In the PowerOffCmd process ther is some operation on object like list which gives error with calling thread...

Upvotes: 1

Views: 1996

Answers (1)

scharette
scharette

Reputation: 9977

Your probem is that you try to invoke the dispatcher in the awaiting call instead of calling it where you need to work on UI.

Basically, if you do

await Task.Run(() =>
{
    this.Dispatcher.Invoke(new Action(() =>
    {
        if (stnWindowVM.PowerOffCmd.CanExecute(null))
        {
            stnWindowVM.PowerOffCmd.Execute(null);
        }
    }));
});

You're doing it wrong since you're processing your async work on the UI thread the whole time.

One work around you can try is invoke the dispatcher in your PowerOffCmd where it gives you the error. This way, everything except where you need to work on UI thread (modifying controls for example) will execute on a separate thread which will assure that you're UI stay responsive.

Upvotes: 1

Related Questions