2012-04-22 30 views
1

我正在編寫一個家庭WPF應用程序,它以配置的間隔從服務器獲取文件。更新Timer.Elapsed事件中的MainWindow

這是一個基本的窗口,有幾個標籤。我有以下

  • 開始時間(反映的DateTime「開始」事件被擊中
  • 持續時間(反映應用程序已經運行的時間)
  • 速度(該文件的下載速度)

我想要更新的主窗口上持續時間各第二,所以我有下面的代碼來做到這一點(在一個單獨的類「RunDownloader.cs」)。

private void StartTickTimer() 
    { 
     const double interval = 1000; 

     if (_tickTimer == null) 
     { 
      _tickTimer = new Timer 
      { 
       Interval = interval 
      }; 
      _tickTimer.Elapsed += _ticktimer_Elapsed; 
     } 

     _tickTimer.Start(); 
    } 

On _ticktimer_Elapsed我在主窗口調用了一個方法_mainWindow.UpdateTicker();

這會執行以下操作。

public void UpdateTicker() 
    { 
     var timeStarted = lblTimeStarted.Content.ToString(); 
     DateTime startTime = DateTime.Parse(timeStarted); 
     TimeSpan span = DateTime.Now.Subtract(startTime); 

     //ToDo: Output time taken here! 
     //lblTimeElapsed.Content = 
    } 

我有兩個問題。

  1. 我在調用lblTimeStarted.Content.ToString()時發生以下異常:在UpdateTicker()

    "The calling thread cannot access this object because a different thread owns it." 
    
  2. 我不很清楚,如何從時間跨度正確顯示持續時間lblTimeElapsed.Content

感謝您事先的任何答案。 :D

回答

5

在WPF中,您不能從UI線程以外的線程更新UI對象(即在UI線程上創建的UI對象)。
爲了從其他線程(例如計時器線程)更新UI控件,您需要使用Dispatcher在UI線程上運行更新代碼。
Question/answer可能會幫助你,或者你會發現大量的搜索「WPF調度員」的信息。
一個例子調度呼叫 - 將LAMDA代碼將得到付郵到UI線程上運行:

Dispatcher.BeginInvoke(new Action(() => 
{ 
    text_box.AppendText(formated_msg); 
    text_box.ScrollToEnd(); 
})); 

或者你可以用DispatchTimer取代現有的計時器 - 不像計時器您使用它確保定時器回調是在UI線程上:

理由使用而不是一個System.Timers.Timer一個DispatcherTimer是在DispatcherTimer運行同一個線程調度程序上和的DispatcherPriority可以在DispatcherTimer進行設置。

+0

謝謝你的回答工作一種享受。 UI正在按預期更新。 – 2012-04-22 13:11:15

1
  1. 你的計時器在自己的線程運行,並從那裏調用UpdateTicker()方法。但是,大多數UI框架(包括WPF)都禁止從其他線程(而不是相應控件創建的線程)(後者通常表示爲「UI線程」)訪問UI控件。您在這裏有兩個主要選項:

    • 使用DispatcherTimer。這將在您的UI線程上運行並避免任何線程問題,但是,由於您的UpdateTicker()代碼也在此線程上運行,因此在進行處理時,您的UI將不響應。這可能是也可能不是問題;如果你所做的只是一些場地/財產變化,這沒什麼問題。
    • 在您的定時器回調中,使用this.Dispatcher.Invoke()this.Dispatcher.BeginInvoke()在其他處理完成後調用您的UI更新方法(例如:this.Dispatcher.Invoke((Action) UpdateTicker))。這會將「調用」調用到適當的UI更新線程,同時保持數據處理的異步性。換句話說,這是一種更有效的方法。
  2. TimeSpan結構有一個ToString()方法,接受格式化;或者,如果這很不方便,它有幾個幫助屬性(DaysHours,Minutes,TotalDays,TotalHours,TotalMinutes等),您可以用於顯示目的。

0

你可以不喜歡它: 在主窗口:

public void ChangeTime(string time) 
     { 
      lblsb.Content = time; 
     } 

而且在RunDownloader:

class RunDownloader 
    { 
     Timer _tickTimer; 
     MainWindow window; 

     public RunDownloader(MainWindow window) 
     { 
      this.window = window; 
     } 

     private delegate void MyDel(string str); 

     public void StartTickTimer() 
     { 
      const double interval = 1000; 

      if (_tickTimer == null) 
      { 
       _tickTimer = new Timer 
       { 
        Interval = interval 
       }; 
       _tickTimer.Elapsed += (object sender, ElapsedEventArgs e) => 
        { 
         window.Dispatcher.BeginInvoke(new MyDel(window.ChangeTime), DateTime.Now.ToLongDateString()); 
        }; 
      } 

      _tickTimer.Start(); 
     } 

    }