新的调度程序计时器与旧计时器一起创建,只有新的应该运行

本文关键字:计时器 运行 一起 调度程序 创建 | 更新日期: 2024-12-05 17:18:47

前几天我遇到了一个问题。我已经找到了为什么会发生这种情况,但我从未遇到过这样的问题,所以我不知道如何解决它。

我有一个应用程序,在仪表板视图(主视图)中,在仪表板视图模型中启动了调度程序计时器。当计时器滴答作响时,我们从数据库中获取数据,此列表在视图和视图模型之间数据绑定。当有新数据导致数据库更改时,将播放声音。

用户可以转到其他视图。当用户返回到仪表板视图时,将再次创建仪表板视图模型,调度程序计时器也是如此。
现在有 2 个计时器,它们都触发 Tick 事件,为用户创造了一个令人困惑的场景。

这是我对应用程序中现在发生的事情的观察:
我的计时器每分钟滴答作响。当我启动应用程序时,仪表板视图 #1 打开。DashboardViewModel #1 启动,DispatcherTimer #1 也启动。
我切换到其他视图,并对数据进行更新(新电子邮件),因此当计时器滴答作响时,DashboardView 中的列表将更改并播放声音。
当计时器 #1 处于 30 秒时,我切换到新创建的 DashboardView,从而创建 View&ViewModel&Timer #2。1 分钟后,计时器 #1 滴答作响,有新数据,因此它会更新数据库并播放声音,但视图中的列表不会更新。我认为这是因为视图 #2 显示在 #1 上。我知道,因为否则我会看到一个覆盖层说它令人耳目一新。
视图 #2 数据绑定到 视图模型 #2。计时器 #1 更新了视图模型 #1,因此更改不会显示,因为我们看不到视图 #1,因为它被视图 #2 替换/重叠。1 分 30 秒后,计时器 #2 滴答作响,从数据库获取数据,不播放声音,因为计时器 #1 已使数据库更新,并显示处于新状态的数据。
(我希望这是有道理的)

所以,TLDR:有 2 个计时器正在运行,而只有 1 个应该处于活动状态(我认为是最新的)。我怎样才能做到这一点?

这是我现在拥有的DashboardViewModel的(部分):

namespace QRM.ViewModel
{
    class DashboardListViewModel : INotifyPropertyChanged
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        DBServer dbServer = new DBServer();
        #region Constructor
        public DashboardListViewModel()
        {
            log.Info("Dashboard Initializing - Starting...");
            MyObservableCollection<View_server> listDashboard = new MyObservableCollection<View_server>();
            ListDashboard = dbServer.ReadDashboard();
            listBoxCommand = new RelayCommand(() => SelectionHasChanged());
            // Refresh to get all new emails, errors, etc.
            GetListDashboard();
            IsRefreshing = Visibility.Collapsed;
            // Make a timer to renew the data in the Dashboard automatically. 
            DispatcherTimer timer = new DispatcherTimer();
            timer.Tick += new EventHandler(timer_Tick);
            timer.Interval = Properties.Settings.Default.Timer_interval; // hours, minutes, seconds.
            timer.Start();
            //Receive the Notification sent after DashboardDetailsViewModel has handled the button commands, and call a respond method for the List.
            App.Messenger.Register("RefreshServers", (Action)(() => GetListDashboard()));
            App.Messenger.Register("ClearSelection", (Action)(() => SelectedServer = null));
            App.Messenger.Register("ErrorSolved", (Action)(() => KeepSelection(selectedServer)));
            App.Messenger.Register("WarningSound", (Action)(() => HasNewError = true));
            log.Info("Dashboard Initializing - Done.");
        }
        #endregion
        #region Get list dashboard
        private void GetListDashboard()
        {
            HasNewError = false;
            log.Info("Dashboard - Checking for Email...");
            // The old Outlook class and methods
            //EmailManager checkMail = new EmailManager();
            //checkMail.GetEmail();
            // First, check for mail.
            IMAPManager checkMail = new IMAPManager();
            checkMail.GetEmail();
            log.Info("Dashboard - Checking for linked Errors...");
            // Check if the emails have Errors linked to them. If not, add the Error from the Email to the DB
            ErrorManager checkError = new ErrorManager();
            checkError.GetNewErrors();
            log.Info("Dashboard List - Starting...");
            // Load the dashboard.
            ListDashboard = dbServer.ReadDashboard();
            System.Diagnostics.Debug.WriteLine("REFRESHED THE DASHBOARD");
            log.Info("Dashboard List - Done.");
        }
        private void KeepSelection(View_server keepSelection)
        {
            GetListDashboard();
            SelectedServer = keepSelection;
            SelectionHasChanged();
        }
        #endregion
        #region Timer
        //This method runs every time the timer ticks.
        private async void timer_Tick(object sender, EventArgs e)
        {
            log.Info("Dashboard - Refreshing...");
            System.Diagnostics.Debug.WriteLine(">>Timer tick");
            IsRefreshing = Visibility.Visible;
            // To make sure the overlay is visible to the user, let it be on screen for at least a second (2x half a second)
            await Task.Delay(500);
            if (selectedServer != null)
            {
                KeepSelection(selectedServer);
            }
            else
            {
                GetListDashboard();
            }
            // 2nd half second.
            await Task.Delay(500);
            IsRefreshing = Visibility.Collapsed;
            if (hasNewError == true)
            {
                System.Diagnostics.Debug.WriteLine("List has new error");
                PlayWarningSound();
                HasNewError = false;
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("List has no new error");
                HasNewError = false;
            }
            System.Diagnostics.Debug.WriteLine(">>End timer");
            log.Info("Dashboard - Refreshed.");
        }        
        #endregion
    }
}

新的调度程序计时器与旧计时器一起创建,只有新的应该运行

这里有一些问题。让我们先从最基本的开始:

清理

释放或关闭DashboardListViewModel时,需要断开DispatcherTimer.Tick事件处理程序的连接,调用.Stop(),然后调用.Finalize()。MSDN.这将确保您的System.Windows.Threading.DispatcherTimer得到正确清理。

Async/Await & Event Handlers

此外,DispatcherTimer.Tick事件处理程序定义为 async void 。这是 async 关键字的错误用法。而是使用这个:

private void timer_Tick(object sender, EventArgs e)
{
    log.Info("Dashboard - Refreshing...");
    System.Diagnostics.Debug.WriteLine(">>Timer tick");
    IsRefreshing = Visibility.Visible;
    // To make sure the overlay is visible to the user, let it be on screen for at least a second (2x half a second)
    Thread.Sleep(500);
    if (selectedServer != null)
    {
        KeepSelection(selectedServer);
    }
    else
    {
        GetListDashboard();
    }
    // 2nd half second.
    Thread.Sleep(500);
    IsRefreshing = Visibility.Collapsed;
    if (hasNewError == true)
    {
        System.Diagnostics.Debug.WriteLine("List has new error");
        PlayWarningSound();
        HasNewError = false;
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("List has no new error");
        HasNewError = false;
    }
    System.Diagnostics.Debug.WriteLine(">>End timer");
    log.Info("Dashboard - Refreshed.");
}

我通常从不建议使用Thread.Sleep但是由于您已经在线程计时器的上下文中,这是有道理的。

最后一个问题

您确定可以多次调用App.Messenger.Register,就像每次实例化视图模型时一样?我本以为,在static的背景下,你只会想做一次。