【问题标题】:How to show progress during app startup in Xamarin Forms如何在 Xamarin Forms 中显示应用启动过程中的进度
【发布时间】:2016-07-23 23:33:45
【问题描述】:

我有一个 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 秒的延迟。实际上,发生的情况是我从来没有看到第 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();
}

【问题讨论】:

    标签: c# xamarin async-await xamarin.forms doevents


    【解决方案1】:

    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 中的属性,这样你的代码会更干净,你不必担心线程,我认为

    我设法注意到的另一点是命名约定。简而言之,任何等待的东西都应该有一个名字+“Async”作为后缀,所以它会是

    await DoStep1Async();
    

    【讨论】:

    • 感谢您的想法。我上面的代码示例已经过简化,但我的真实代码确实以您描述的方式正确使用了视图模型和异步命名约定。但是,我的异步方法必须异步执行。如果我按照您显示的方式调用它们,那么显式异步的位(例如 HTTP API 调用)将无法执行。
    • 你能在 github 中制作一个生成问题的示例,以便我们看看吗?注意我个人只能在有问题的情况下使用 android 我可以帮助你
    • 我已经更新了我的问题,以展示我能想象到的最简单的案例来说明问题。此示例甚至没有尝试处理调用异步方法的问题 - 一切都是同步的 - 但它仍然无法按预期工作。
    • 抱歉浪费您的时间。我刚刚使用您推荐的异步模式重新测试了我的简化示例并且它有效!显然,我在现实世界的应用程序中还有一些其他问题,但你的技术确实解决了我的问题中描述的问题。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 2019-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多