在Timer.Elapsed事件上更新MainWindow

Joe*_*Bob 1 c# wpf

我正在编写一个家庭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();
    }
Run Code Online (Sandbox Code Playgroud)

在_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 =
    }
Run Code Online (Sandbox Code Playgroud)

我有两个问题.

  1. 调用lblTimeStarted.Content.ToString()时,我有以下异常; 在UpdateTicker()中

        "The calling thread cannot access this object because a different thread owns it."
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我不太清楚,如何正确显示来自TimeSpan的lblTimeElapsed.Content的持续时间

提前感谢您的任何答案.:d

Ric*_*bob 5

在WPF中,您无法从UI线程以外的线程更新UI对象(在UI线程上创建).
为了从其他某个线程(例如计时器线程)更新UI控件,您需要使用Dispatcher在UI线程上运行更新代码.
这个问题/答案可以帮助您,或者您将通过Google搜索"WPF Dispatcher"找到大量信息.
一个示例调度程序调用 - lamda代码将被发布以在UI线程上运行:

Dispatcher.BeginInvoke(new Action(() =>
{
    text_box.AppendText(formated_msg);
    text_box.ScrollToEnd();
}));
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用DispatchTimer替换现有计时器- 与您使用它的计时器不同,确保计时器回调位于UI线程上:

使用与System.Timers.Timer相对的DispatcherTimer的原因是DispatcherTimer在与Dispatcher相同的线程上运行,并且可以在DispatcherTimer上设置DispatcherPriority.