1

構成された間隔でサーバーからファイルを取得するホーム WPF アプリを作成しています。

これは基本的なウィンドウで、いくつかのラベルがあります。私は次のものを持っています

  • 開始時刻 (「開始」イベントが発生した DateTime を反映します)
  • 期間 (アプリの実行時間を反映)
  • 速度 (ファイルのダウンロード速度)

メインウィンドウの期間を毎秒更新したいので、これを行うための次のコードがあります(別のクラス「RunDownloader.cs」内)。

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

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

        _tickTimer.Start();
    }

_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

4

3 に答える 3

6

WPF では、UI スレッド以外のスレッドから UI オブジェクト (UI スレッドで作成される) を更新することはできません。
他のスレッド (タイマー スレッドなど) から UI コントロールを更新するには、Dispatcher を使用して UI スレッドで更新コードを実行する必要があります。
この質問/回答が役立つか、「WPF Dispatcher」をグーグルで検索すると、多くの情報が見つかります。
ディスパッチャー呼び出しの例 - ラムダ コードはポストオフされ、UI スレッドで実行されます。

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

または、既存のタイマーをDispatchTimerに置き換えることもできます。使用しているタイマーとは異なり、タイマー コールバックが UI スレッド上にあることが保証されます。

System.Timers.Timer ではなく DispatcherTimer を使用する理由は、DispatcherTimer が Dispatcher と同じスレッドで実行され、DispatcherPriority を DispatcherTimer に設定できるためです。

于 2012-04-22T12:45:06.333 に答える
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()を受け入れるメソッドがあります。または、これが不便な場合は、表示目的で使用できるいくつかのヘルパー プロパティ ( 、 、、 など)DaysHoursありMinutesます。TotalDaysTotalHoursTotalMinutes

于 2012-04-22T12:56:07.363 に答える
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();
        }

    }
于 2012-04-22T13:07:19.697 に答える