发生OnClick_Event时立即更新UI

bro*_*and 3 c# multithreading ui-thread xamarin xamarin.forms

很简单的例子:

<StackLayout>
  <Button Text="{Binding LoginButtonText}" Clicked="Button_Clicked"></Button>
</StackLayout>
Run Code Online (Sandbox Code Playgroud)

代码背后:

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();
  }
} 
Run Code Online (Sandbox Code Playgroud)

我正在使用MVVM - INotifyPropertyChanged

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

ObservableObject实现可以在这里看到:https://codeshare.io/G87N74

当我点击该按钮几秒钟(4或5)按钮上的文字获取价值"结束2017年3月8日8点55分33秒"后(将它取决于课程的当前时间戳).按钮文本Start + DateTime不会出现.

如果我写这个:

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();
}
Run Code Online (Sandbox Code Playgroud)

但它不是100%,因为我们不知道线程将如何安排执行.当我们点击事件方法时,是否有一种简单的方法可以立即更新UI

有一个方法,Device.BeginInvokeOnMainThread()但它返回void,因此它不会阻止UI.

Paw*_*rek 5

我们无法立即从后台线程对UI进行更改.来自后台线程的UI线程上的所有操作将在下一个UI线程循环中执行.如果您的应用程序没有被某些密集型任务阻止,它将尽可能地立即接近我们.

如果你想让你的第一个例子运作良好,那就把那些繁重的操作放到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();
Run Code Online (Sandbox Code Playgroud)

这里还有来自Xamarin关于线程的文档:

应用程序用户界面始终是单线程的,即使在多线程设备中 - 只有一种屏幕表示,所显示的内容的任何更改都需要通过单个"访问点"进行协调.这可以防止多个线程同时尝试更新同一个像素(例如)!