Xamarin Forms等待时更改按钮文本

8
我正在使用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";
}

有一种方法可以在代码完成之前“强制”更新文本为“等待”吗?

UI线程被您的代码执行阻塞了。您需要在单独的线程中修改按钮文本。 - Sandeep Bansal
感谢您提供的信息,Sandeep! - Leonardo Lima Almeida
3个回答

5
    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";


    }

有没有办法在Btn1_clicked方法中更新按钮的文本。 Device.BeginInvokeOnMainThread返回void,所以我们无法等待它(await)。 - broadband
btn1.Text在函数结束之前确实会发生变化。这就是这个问题的重点。 - Yuri S
我正在使用Visual Studio 2017 + Android 6.0。在调试时,我注意到了上述行为。当我退出Btn1_Clicked方法时,文本按钮会更新。 - broadband
@YuriS,你能再说一遍想要的行为吗? - broadband
是的,但第一个值“Wait”在用户界面中反映出来了吗? - broadband
显示剩余8条评论

2
主要意思是,你需要进行异步操作,但这些操作不能在UI线程上执行。Yuri的解决方案可以实现这一点,但你也可以在执行异步操作后,如果需要更新UI,只需在UI线程上进行以防止UI锁定。
真正的问题源于线程的工作方式。如果想实时查看,请打开Xamarin Stud中的Threads调试面板,并在代码调试过程中观察线程。
让我们以你的代码为例:
private void Button_Clicked(object sender, EventArgs e)
{
    var btn = (Button)sender;
    btn.Text = "Wait";
    ...

当我们进入“Button_Clicked”事件处理程序时,我们现在位于UI线程上。我们如何知道呢?因为用户点击了按钮,我们就在这里。所有内容都在主UI线程上运行。除非操作系统确定需要管理线程(例如性能等),否则我们在代码中接下来执行的任何操作都将继续在该线程上运行。
如果您的某些代码执行异步任务,例如发送Web请求调用以从REST API获取一些数据,则需要注意您所在的线程。我们无法保证我们的Web请求调用将在UI线程或任何线程上运行,除非手动处理它。这就是我们先进的操作系统的好处,它为我们处理多核多线程处理。
因此,我们希望确保对UI的任何更新都发生在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 线程。另一种选择是获取我们的 UI 线程的上下文并将其缓存以在进行更新时使用。这通常在 Xamarin.Android 和 Xamarin.iOS 应用程序中完成。
真正的方法是通过数据绑定来完成所有这些操作。Xamarin.Forms 是有意作为 Model-View-ViewModel(MVVM)模式的一部分构建的。当您调用 OnPropertyChanged 时,Xamarin.Forms 绑定会为您处理所有上下文切换。这样,您只需更新与按钮文本绑定的 ViewModel 中的属性即可。
希望这有助于更好地理解发生在线程级别上的情况!
披露:我在 Xamarin/Microsoft 工作

1
我不想在这里引发讨论,但是... 1. "一些代码..." 可以执行同步操作,这就是为什么提出了这个问题。2. 在你的例子中,没有必要将 btn.Text="Do Something"; 包装在 BeginInvokeOnMainThread 中。在 Btn1_Clicked 中,您可以像上面为 "Wait" 做的那样设置文本。3. 你说在我的例子中所有东西都在 UI 线程上工作。错了。看看 Task.Run。它被调用是因为我让 "一些代码..." 成为异步的。你真的为 Xamarin 工作吗?我真的很怀疑。 - Yuri S
1
你为什么将按钮处理程序设为异步的?我没有看到任何等待。你假设“某些代码”是异步的,如果它不是异步的呢? - Yuri S
@YuriS 我不想和你争论这个问题。我已经根据你的评论编辑了我的回复。完成。 - BrewMate
如果你让“...some code.”同步,你将永远不会看到“等待”,直到你的代码完成,然后立即会看到“做某事”。 - Yuri S

2
在Xamarin.forms中使用以下方式:
  Device.BeginInvokeOnMainThread(() =>
       {
      Task.Delay(4000).Wait();
            btn.Text = "Do Something";
       });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接