很简单的例子:
<StackLayout>
<Button Text="{Binding LoginButtonText}" Clicked="Button_Clicked"></Button>
</StackLayout>
代码背后:
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();
}
}
我使用MVVM - INotifyPropertyChanged
public class ItemsViewModel : ObservableObject
{
private string _loginButtonText;
public string LoginButtonText
{
get { return _loginButtonText; }
set { SetProperty(ref _loginButtonText, value); }
}
}
ObservableObject实现可以在这里看到:https://codeshare.io/G87N74
当我在几秒钟(4或5)后点击按钮时,按钮文字获得价值&#39;结束03/08/2017 08:55:33&#39; (它当然取决于当前的时间戳)。按钮文本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();
}
但它不是100%,因为我们不知道线程将如何安排执行。当我们点击事件方法时,是否有一种简单的方法立即更新UI ?
有一个方法Device.BeginInvokeOnMainThread()
,但它返回void,因此它不会阻止UI。
答案 0 :(得分: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();
此处还有来自Xamarin的关于线程的文档:
应用程序用户界面始终是单线程的,即使在多线程设备中 - 只有一种屏幕表示,所显示的内容的任何更改都需要通过单个“访问点”进行协调。这可以防止多个线程同时尝试更新同一个像素(例如)!