我有一个Xamarin Forms应用程序需要在启动期间执行一系列长时间运行的操作。我想告诉用户进度,使用带有简单状态消息的页面随着序列的进展而变化。
操作序列是作为一组异步方法实现的,我将其包装在异步容器方法中,该方法还包含更新UI的代码。这是一个简化版本:
private async Task DoStartupSequence()
{
SetStatusMessage("Step 1");
await DoStep1();
SetStatusMessage("Step 2");
await DoStep2();
SetStatusMessage("Step 3");
await DoStep3();
SetStatusMessage("Completed");
}
但是,无论我如何调用序列,在序列执行完成之前,UI都不会更新。
我尝试在App
构造函数中运行自己的线程序列:
public App()
{
InitializeComponent();
Task.Run(() => DoStartupSequence()).Wait();
}
我也试过在OnStart
方法中异步调用序列:
protected override async void OnStart()
{
await DoStartupSequence();
}
在WinForms应用程序中,可以使用Application.DoEvents
解决此问题(虽然我知道这种方法有其自身的问题)。无论如何,Xamarin Forms似乎不支持这样的任何内容。
这似乎是一个普遍的要求,我想它的实现必须有一个标准模式。有谁知道它是什么?
非常感谢,
添
更新:为了回应@BraveHeart的建议,我可以进一步简化测试代码,以显示哪些内容无法正常运行。
以下示例引用了2个静态页面,每个页面仅包含一个标签。第1页宣布长时间运行序列的开始,第2页宣布完成。我的目标是看到这两个页面在第一个和第二个之间延迟1秒。在实践中,发生的事情是我从未看到第1页,因为两个页面都是一起渲染的。
public App()
{
InitializeComponent();
var navigationPage = new NavigationPage();
MainPage = navigationPage;
navigationPage.Navigation.PushAsync(new TestPage1(), false).Wait();
Task.Delay(1000).Wait();
navigationPage.Navigation.PushAsync(new TestPage2(), false).Wait();
}
答案 0 :(得分:2)
UI中的更改应仅通过UI线程进行。
public App()
{
InitializeComponent();
Task.Run(() => DoStartupSequence()).Wait();
}
这段代码告诉程序创建另一个任务(线程)来执行 DoStartupSequence(),包括 SetStatusMessage(),我认为它只是设置了一个内容一个标签或类似的东西,不会有其他等待的东西。
所以试着这样做
public App()
{
InitializeComponent();
StartupSequence();
}
private async void StartupSequence()
{
SetStatusMessage("Step 1");
await DoStep1();
SetStatusMessage("Step 2");
await DoStep2();
SetStatusMessage("Step 3");
await DoStep3();
SetStatusMessage("Completed");
}
我认为这可以解决您的问题。但是,我的建议是将“DoSteps”的代码放在viewmodel级别,并将标签的内容绑定到viewmodel中的属性,这样你的代码就会更加清晰,而你不必担心线程,我认为。
我设法注意的另一点是命名约定。简而言之,任何等待的东西都应该得到一个名称+“异步”作为后缀,所以它将是
await DoStep1Async();