我正在使用Xamarin Forms开发跨平台应用程序。
我想将“Do Something”按钮的文本更改为“等待”,而代码正在处理例程,并在代码完成运行后返回“Do Something”。
问题是:按钮文本仅在代码完成后更改。
简单例子:
private void Button_Clicked(object sender, EventArgs e)
{
var btn = (Button)sender;
btn.Text = "Wait";
...some code..
btn.Text = "Do Something";
}
在代码完成之前有一些方法可以“强制”将文本更新“等待”吗?
答案 0 :(得分:3)
private async void Btn1_Clicked(object sender, EventArgs e)
{
btn1.Text = "Wait";
await Task.Run(async () =>
{
for (int i = 1; i <= 5; i++)
{
//if your code requires UI then wrap it in BeginInvokeOnMainThread
//otherwise just run your code
Device.BeginInvokeOnMainThread(() =>
{
btn1.Text = $"Wait {i}";
});
await Task.Delay(1000);
}
});
btn1.Text = "Done";
}
答案 1 :(得分:2)
重点是您希望在UI线程上等待不发生的异步操作。 Yuri的解决方案可以做到这一点,但您也可以进行异步操作,然后如果您需要对UI进行任何更新,您只需要在UI-Thread上进行以防止UI锁定。
真正的问题来自你的线程如何工作。如果你想看到这个实时,请拉开Xamarin Stud中的Threads调试板,并在逐步执行代码时观察线程。
我们以您的代码为例:
private void Button_Clicked(object sender, EventArgs e)
{
var btn = (Button)sender;
btn.Text = "Wait";
...
当我们进入&#34; Button_Clicked&#34;事件处理程序,我们现在在UI-Thread上。我们怎么知道?因为用户点击了按钮,我们就在这里。一切都在主UI线程上运行。我们代码中的下一步操作将继续在该线程上运行,除非操作系统确定由于某种原因(性能,操作系统的工作方式等)需要管理线程。
如果您的某些代码执行某些异步任务,例如发送Web请求调用以从REST API获取某些数据,则需要了解您所使用的线程。我们无法保证我们的Web请求调用将在UI-Thread或任何线程上运行而无需手动处理。这是我们先进的操作系统的好处,它为我们处理多核多线程处理。
出于这个原因,我们希望确保在UI-Thread上对UI进行任何更新。您的代码的正确解决方案如下:
private async void Btn1_Clicked(object sender, EventArgs e)
{
var btn = (Button)sender;
btn.Text = "Wait";
...some code...maybe async or not (take out async above if not)
Device.BeginInvokeOnMainThread(() =>
{
btn.Text = "Do Something";
});
}
这对我们有用,因为我们告诉Xamarin.Forms在主线程上运行该命令。 Xamarin.Forms以这种方式公开主UI-Thread。另一种选择是获取UI-Thread的上下文并在进行更新时使用缓存。这通常在Xamarin.Android和Xamarin.iOS应用程序中完成。
实现所有这些的真正方法是通过数据绑定。构建Xamarin.Forms的目的是与Model-View-ViewModel(MVVM)模式一起使用。当您调用OnPropertyChanged
时,Xamarin.Forms绑定会为您处理所有上下文切换。这样您就可以更新ViewModel中绑定到按钮文本的属性。
我希望这有助于更好地理解线程级别上发生的事情!
披露:我为Xamarin / Microsoft工作
答案 2 :(得分:0)
在Xamarin.forms中使用这种方式
Device.BeginInvokeOnMainThread(() =>
{
Task.Delay(4000).Wait();
btn.Text = "Do Something";
});