【问题标题】:Why isn't my event "myTimer_Elapsed updating the label on my device?为什么我的事件“myTimer_Elapsed 没有更新我设备上的标签?
【发布时间】:2019-10-26 11:54:54
【问题描述】:

如果有人问过这个问题,我很抱歉。我找不到这个问题的具体答案并且被卡住了。我现在正在学习计时器。我是一名 VBA 程序员,涉足 C#。

我现在正在尝试编写一个跨平台应用程序,但我的 myTimer.Elapsed 事件无法正常更新标签。

我已阅读来自goalkicker.com 的C# 专业人士注意事项中的计时器章节,并尝试复制他们的倒数计时器。我还阅读了 Microsoft API for Timer.Elapsed Event。两者都没有给我一个明确的答案,说明我哪里出错了。谷歌也不太友好,因为我可能查询不正确。

我尝试停止计时器,只允许该方法运行,直接在我的 Elapsed 方法中写入标签并在单独的方法中更新标签(如代码中所示)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Threading;
using System.Timers;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Essentials;
using Plugin.Geolocator;

namespace LocationServices
{
    public partial class MainPage : ContentPage
    {
        public int timeLeft = 3;
        public Timer myTimer = new Timer();

        SensorSpeed speed = SensorSpeed.UI; //ignore

        public MainPage()
        {
            InitializeComponent();
            cmdGetLocation.Clicked += GetLocation; //ignore
            cmdStartTimer.Clicked += StartTimer;
            Accelerometer.ReadingChanged += MainPage_ReadingChanged; //ignore

            InitializeTimer();
        }

        private void UpdateTimeLeftLabel(string NumberToDisplay)
        {
            txtTimeRemaining.Text = "Time left: " + NumberToDisplay;
        }

        private void InitializeTimer()
        {
            myTimer.Interval = 1000;
            myTimer.Enabled = true;
            myTimer.Elapsed += MyTimer_Elapsed;
            UpdateTimeLeftLabel(timeLeft.ToString()); //working just fine
        }

        private void MyTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            myTimer.Stop();
            UpdateTimeLeftLabel(timeLeft.ToString()); //this one is not working.

            if (timeLeft <= 0)
            {
                myTimer.Dispose();
            }
            else
            {
                timeLeft -= 1;
                myTimer.Start();
            }
        }

        private void StartTimer(object sender, EventArgs e)
        {
            myTimer.Start();
        }
    }
}

我的计时器事件正在触发,因为断点按预期命中。正在调整 timeLeft 变量,已在即时窗口中验证。只是标签没有更新。

【问题讨论】:

  • 哦!我还更改了我的 StartTimer 方法以直接更改标签而不实际调用 myTimer.Start() 并且成功更新了我希望更新的标签。
  • 所以,你可以回答你自己的问题。

标签: c# xamarin.forms cross-platform visual-studio-2019 system.timers.timer


【解决方案1】:

使用BeginInvokeOnMainThread 强制您的 UI 代码在 UI 线程上运行

private void UpdateTimeLeftLabel(string NumberToDisplay)
    {
        Device.BeginInvokeOnMainThread( () => {
          txtTimeRemaining.Text = "Time left: " + NumberToDisplay;
        });
    }

【讨论】:

  • 杰森,这行得通。我很欣赏这个解决方案。不过,我哪里做错了?是不是我对线程不够精通?我了解线程的概念以及多线程是什么,但我似乎还不能完全理解线程的使用。
  • UI 更新只能从主 UI 线程完成。您正在使用的 Timer 在后台线程上触发。 BeginInvokeOnMainThread 只是强制您的代码在 UI 线程上运行,以便它可以更新 UI。
【解决方案2】:

只是在这里添加更多信息作为支持信息

.NET 包含四个名为 Timer 的类,每个类都提供不同的功能:

  • System.Timers.Timer,它会触发一个事件并定期在一个或多个事件接收器中执行代码。该类旨在用作多线程环境中的基于服务器或服务组件;它没有用户界面,在运行时不可见。
  • System.Threading.Timer,它定期在线程池线程上执行单个回调方法。回调方法是在定时器实例化时定义的,不能更改。与 System.Timers.Timer 类一样,此类旨在用作多线程环境中的基于服务器或服务组件;它没有用户界面,在运行时不可见。
  • System.Windows.Forms.Timer(仅限 .NET Framework),一种 Windows 窗体组件,可触发事件并定期在一个或多个事件接收器中执行代码。该组件没有用户界面,设计用于单线程环境;它在 UI 线程上执行。
  • System.Web.UI.Timer(仅限 .NET Framework),一个定期执行异步或同步网页回发的 ASP.NET 组件。

现在您的问题是您最有可能使用线程计时器。这意味着您需要返回 UI 线程,因为您无法从另一个线程更新 UI

查看杰森的回答

【讨论】:

  • 所以,这是因为我对线程缺乏了解。今晚我将深入研究那本 C# 书籍。谢谢你们俩! :)
猜你喜欢
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多