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'm using MVVM - INotifyPropertyChanged
public class ItemsViewModel : ObservableObject
{
private string _loginButtonText;
public string LoginButtonText
{
get { return _loginButtonText; }
set { SetProperty(ref _loginButtonText, value); }
}
}
ObservableObject implementation 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 value 'End 03/08/2017 08:55:33' (will it depends on the current timestamp of course). The button text Start + DateTime does not appear.
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 it's not 100% as we don't know how threads will be scheduled for execution. Is there a simple way to update UI immediately when we hit event method?
There is a method Device.BeginInvokeOnMainThread()
but it returns void, therefore it doesn't block UI.