Refresh UI immediately when OnClick_Event occurs

A very simple example:

<StackLayout>
  <Button Text="{Binding LoginButtonText}" Clicked="Button_Clicked"></Button>
</StackLayout>

      

Code behind:

public partial class ItemsPage : ContentPage
{
  private ViewModels.ItemsViewModel _viewModel;
  public ItemsPage()
  {
    _viewModel = new ItemsViewModel();
    BindingContext = _viewModel;
    InitializeComponent();
  }
  private void Button_Clicked(object sender, EventArgs e)
  {         
    this._viewModel.LoginButtonText = "Start" + DateTime.Now.ToString();
    // this loop is just for testing purposes. To demonstrate 
    // that this loop block UI thread
    for (int i = 0; i < 100; i++)
    {
      for (int j = 0; j < 1000; j++)
      {
        string s = new Random(45).NextDouble().ToString();
      }
    }
    this._viewModel.LoginButtonText = "End " + DateTime.Now.ToString();
  }
} 

      

I am using MVVM - INotifyPropertyChanged

public class ItemsViewModel : ObservableObject
{ 
  private string _loginButtonText;
  public string LoginButtonText
  {
    get { return _loginButtonText; }
    set { SetProperty(ref _loginButtonText, value); }
  }
}

      

ObservableObject execution can be seen here: https://codeshare.io/G87N74

When I click on the button after a few seconds (4 or 5), the button text gets the value "End 03/08/2017 08:55:33" (this will of course depend on the current timestamp). Start + DateTime button text is not displayed.

It works if I write this:

private void Button_Clicked(object sender, EventArgs e)
{
  this._viewModel.LoginButtonText= "Start" + DateTime.Now.ToString();
  // I assume here execution switches threads as it sees Task as a new thread. While waiting for task to finish
  // it finished mvvm INotifyPropertyChanged change.Invoke call and updates the button text
  await Task.Delay(5000);
  this._viewModel.LoginButtonText = "End " + DateTime.Now.ToString();
}

      

But this is not 100% since we do not know how the threads will be scheduled to run. Is there an easy way to update the interface immediately when we hit the event method?

There is a method Device.BeginInvokeOnMainThread()

, but it returns void, so it doesn't block the UI.

+3


source to share


1 answer


We cannot make changes to the UI directly from the background thread. All operations on the UI thread from the background thread will be performed in the next cycle of the UI threads. And if your application is not blocked by some intensive tasks, it will be as close to immediately as we can achieve.

If you want your first example to work well, put these heavy operations on the backgorund:

this._viewModel.LoginButtonText = "Start" + DateTime.Now.ToString();

//With await Task.Run (creating new thread) on heavy operation this thread is not blocked
//so previous operation is scheduled to UI thread and changes will appear on screen
await Task.Run(() =>
{
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 1000; j++)
        {
            string s = new Random(45).NextDouble().ToString();
        }
    }
});
this._viewModel.LoginButtonText = "End " + DateTime.Now.ToString();

      



Also here is the documentation from Xamarin about streams:

Application user interfaces are always single-threaded, even in multi-threaded devices - only one screen view and any changes to what is displayed must be coordinated through a single "access point". This allows multiple threads to try to update the same pixel at the same time (for example)!

+5


source







All Articles