2016-04-26 12 views
1

先日問題が発生しました。私はなぜそれが起こっているのか分かったが、私はそれを解決する方法を知らないので、私はそのような問題で実行したことがない。新しいDispatcherTimerは古いものと一緒に作成されますが、新しいものだけを実行する必要があります

私は、DashboardView(メインビュー)でDispatcherTimerがDashboardViewModelで起動されるアプリケーションを持っています。 Timerがチェックされると、データベースからデータが取得され、このリストはViewとViewModelの間でデータバインドされます。新しいデータベースが変更されたデータがあると、サウンドが再生されます。

ユーザーは他のビューに移動できます。ユーザーがDashboardViewに戻ると、DashboardViewModelが再度作成され、DispatcherTimerも作成されます。
今は2つのタイマーがあり、それらはTickイベントを起動し、ユーザーにとって混乱するシナリオを作成します。

これは今のアプリケーションで何が起こるかの私の観察である:
私のタイマーは毎分刻み。アプリケーションを起動すると、DashboardView#1が開きます。 DashboardViewModel#1が起動し、DispatcherTimer#1も起動します。
私は別のビューに切り替えて、データ(新しい電子メール)を更新するので、Timerがチェックされると、DashboardViewのリストが変更され、サウンドが再生されます。
Timer#1が30秒になると、新しく作成されたDashboardViewに切り替わり、View & ViewModel & Timer#2を作成します。 1分後、Timer#1は新しいデータがあり、DBを更新してサウンドを再生しますが、ビュー内のリストは更新されません。 #1よりも#2のビューが上に表示されているからです。そうでなければ、私はそれが爽やかであると言っているオーバーレイを見るだろうから、私は知っている。
View#2はViewModel#2にデータバインドされています。 Timer#1はViewModel#1を更新しました。そのため、View#1がView#2で置き換えられ/オーバーラップしているため、変更が表示されません。 タイマー#2が1分30秒後にタイマー#2がチェックし、DBからデータを取得し、タイマー#1によってDBがすでに最新の状態になっているときにサウンドを再生せず、新しい状態のデータを表示します。
(私は意味を成していることを願っています)

ので、TLDR:唯一の1がアクティブである必要がありながら、実行している2つのタイマーがあります(最新のもの、私は思います)。 これをどうすれば実現できますか?私は今それを持っているよう

はここ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 
    } 
} 
+1

"ユーザーがDashboardViewに戻ると、DashboardViewModelが再度作成されます。"それをしないでください。代わりに、既存のビューモデルインスタンスを再利用します。また、新しいビューモデルのインスタンスを作成できない場合は、以前のインスタンスでタイマーを停止するようにしてください(例: 'IDisposable'を実装し、もちろん' Dispose() 'メソッドを呼び出して、タイマーを停止させます。 – Clemens

+0

ああ。ビュー切り替えは別のソースから取得したものなので、ビュー(モデル)が作成および/または切り替えられた場所とその方法を調べるためには、そのコードを調べなければなりません。しかし、私はそれを理解することができると思う。 – Kailayla

答えて

1

ここで起こっていくつかの問題があります。まずは最も基本的なを見てみましょう:DashboardListViewModelの廃棄またはクローズされ

クリーンアップ

、あなたは、あなたのDispatcherTimer.Tickイベントハンドラを無線化.Stop()を起動してから.Finalize()を呼び出す必要があります。 MSDN。これにより、System.Windows.Threading.DispatcherTimerが適切にクリーンアップされます。

非同期/待つ&イベントハンドラ

また、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の文脈で。

関連する問題