2012-04-22 9 views
1

設定された間隔でサーバーからファイルを取得するホームWPFアプリケーションを作成しています。MainWindowをTimer.Elapsedイベントに更新する

基本的なウィンドウです。いくつかのラベルが付いています。私は、次の

  • 開始時間(アプリが実行されている時間を反映している(日時は、「スタート」イベントが
  • 期間を襲った反映)
  • スピード(ファイルのダウンロード速度)
  • を持っています

は、私は、メインウィンドウの期間を更新する各二私は(別々のクラス「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 = 
    } 

私には2つの問題があります。

  1. lblTimeStarted.Content.ToString();を呼び出すときに次の例外があります。 UpdateTicker()

    "The calling thread cannot access this object because a different thread owns it." 
    
  2. に私はかなりのTimeSpanからlblTimeElapsed.Contentに正しくすべての答えを事前に

おかげで時間を表示する方法を、知りません。 :D

答えて

5

WPFでは、(UIスレッドで作成された)UIオブジェクトをUIスレッド以外のスレッドから更新することはできません。
UIコントロールを他のスレッド(タイマースレッドなど)から更新するには、Dispatcherを使用してUIスレッドで更新コードを実行する必要があります。
Question/answerがあなたを助けたり、「WPF Dispatcher」を検索してたくさんの情報を見つけることができます。
例ディスパッチャコール - ラムダコードがUIスレッド上で実行するためにオフに掲載されます:

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

また、あなたがDispatchTimerで既存のタイマーを置き換えることができます - あなたはそれがタイマーことを保証します使用しているタイマーとは異なりコールバックはUIスレッドである:でSystem.Timers.Timerに反対DispatcherTimerを使用するための

理由はDispatcherTimerがディスパッチャと同じスレッド上で実行されたDispatcherPriorityがDispatcherTimerに設定することができます。

+0

は、あなたの答えは御馳走を働いた、ありがとうございました。 UIが期待どおりに更新されるようになりました。 –

1
  1. あなたのタイマーは、独自のスレッドで実行されているし、そこからUpdateTicker()メソッドを呼び出しています。しかし、WPFを含むほとんどのUIフレームワークは、それぞれのコントロールが作成されたスレッド以外のスレッドからUIコントロールにアクセスすることを禁止します(後者は通常「UIスレッド」と呼ばれます)。ここには2つの主要なオプションがあります:

    • DispatcherTimerを使用してください。これはUIスレッドで実行され、スレッドに関する問題は回避されますが、UpdateTicker()コードもこのスレッドで実行されるため、処理中にUIが応答しなくなります。これは問題かもしれません。あなたがしているのは、フィールド/プロパティの変更のいくつかの場合、これは問題ありません。
    • タイマーコールバックでは、this.Dispatcher.Invoke()またはthis.Dispatcher.BeginInvoke()を使用して、他の処理が完了したらUI更新メソッドを呼び出します(例:this.Dispatcher.Invoke((Action) UpdateTicker))。これにより、データ処理の非同期性を維持しながら、UI更新用の適切なスレッドにコールが「バンプ」されます。言い換えると、これはより効果的なアプローチです。
  2. TimeSpan構造体には、フォーマットを受け入れるToString()メソッドがあります。これが不便な場合は、表示目的で使用できるいくつかのヘルパープロパティ(DaysHoursMinutesTotalDaysTotalHoursTotalMinutesなど)があります。

    あなたはそれが好き行うことができます
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(); 
     } 

    } 
関連する問題