C# 更新计时器上的主窗口。已用事件

C# 更新计时器上的主窗口。已用事件,c#,wpf,C#,Wpf,我正在编写一个家庭WPF应用程序,它以配置的间隔从服务器获取文件 这是一个基本窗口,带有两个标签。我有以下几点 开始时间(反映发生“开始”事件的日期时间 持续时间(反映应用程序已运行的时间) 速度(文件的下载速度) 我想每秒更新主窗口上的持续时间,因此我有以下代码来完成这项工作(在单独的类“RunDownloader.cs”中) 在_ticktimer_已过时,我在主窗口_mainWindow.UpdateTicker()中调用一个方法 这将执行以下操作 public void Up

我正在编写一个家庭WPF应用程序,它以配置的间隔从服务器获取文件

这是一个基本窗口,带有两个标签。我有以下几点

  • 开始时间(反映发生“开始”事件的日期时间
  • 持续时间(反映应用程序已运行的时间)
  • 速度(文件的下载速度)
我想每秒更新主窗口上的持续时间,因此我有以下代码来完成这项工作(在单独的类“RunDownloader.cs”中)

在_ticktimer_已过时,我在主窗口_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 =
    }
我有两个问题

  • 在UpdateTicker()中调用lblTimeStarted.Content.ToString()时出现以下异常

  • 我不太清楚,如何正确显示LBLTIMEEPLASED的持续时间


  • 提前感谢您的回答。:D

    在WPF中,您不能从UI线程以外的线程更新UI对象(在UI线程上创建的对象)。
    为了从其他线程(如计时器线程)更新UI控件,您需要使用Dispatcher在UI线程上运行更新代码。
    这可能会帮助您,或者您可以通过谷歌搜索“WPF Dispatcher”找到大量信息。
    一个调度程序调用示例-lamda代码将被发送到UI线程上运行:

    Dispatcher.BeginInvoke(new Action(() =>
    {
        text_box.AppendText(formated_msg);
        text_box.ScrollToEnd();
    }));
    
    或者,您可以将现有计时器替换为-与您正在使用的计时器不同,它确保计时器回调位于UI线程上:

    Dispatcher.BeginInvoke(new Action(() =>
    {
        text_box.AppendText(formated_msg);
        text_box.ScrollToEnd();
    }));
    
    使用Dispatcher而不是System.Timers.Timer的原因是,Dispatcher与Dispatcher运行在同一线程上,并且可以在Dispatcher上设置DispatcherPriority


    在WPF中,不能从UI线程以外的线程更新UI对象(在UI线程上创建的对象)。
    为了从其他线程(如计时器线程)更新UI控件,您需要使用Dispatcher在UI线程上运行更新代码。
    这可能会帮助您,或者您可以通过谷歌搜索“WPF Dispatcher”找到大量信息。
    一个调度程序调用示例-lamda代码将被发送到UI线程上运行:

    Dispatcher.BeginInvoke(new Action(() =>
    {
        text_box.AppendText(formated_msg);
        text_box.ScrollToEnd();
    }));
    
    或者,您可以将现有计时器替换为-与您正在使用的计时器不同,它确保计时器回调位于UI线程上:

    Dispatcher.BeginInvoke(new Action(() =>
    {
        text_box.AppendText(formated_msg);
        text_box.ScrollToEnd();
    }));
    
    使用Dispatcher而不是System.Timers.Timer的原因是,Dispatcher与Dispatcher运行在同一线程上,并且可以在Dispatcher上设置DispatcherPriority

  • 您的计时器在自己的线程上运行,并从中调用
    UpdateTicker()
    方法。但是,大多数UI框架(包括WPF)禁止从创建相应控件的线程以外的线程访问UI控件(后者通常表示为“UI线程”)。这里有两个主要选项:

    • 使用
      dispatchermer
      。这将在UI线程上运行,并避免任何线程问题,但是,由于
      UpdateTicker()
      代码也在此线程上运行,您的UI在处理时将无响应。这可能是个问题,也可能不是问题;如果您只需对字段/属性进行几次更改,就可以了
    • 在计时器回调中,使用
      this.Dispatcher.Invoke()
      this.Dispatcher.BeginInvoke()
      在其他处理完成后调用UI更新方法(例如:
      this.Dispatcher.Invoke((操作)updatecker)
      ),这将“通气”调用适当的线程进行UI更新,同时保持数据处理的异步性。换句话说,这是一种更有效的方法
  • TimeSpan
    结构有一个接受格式化的
    ToString()
    方法;或者,如果不方便,它有几个助手属性(
    Days
    Hours
    Minutes
    TotalDays
    TotalHours
    TotalMinutes
    等)可用于显示目的的

  • 您的计时器在自己的线程上运行,并从中调用
    UpdateTicker()
    方法。但是,大多数UI框架(包括WPF)禁止从创建相应控件的线程以外的线程访问UI控件(后者通常表示为“UI线程”)。这里有两个主要选项:

    • 使用
      dispatchermer
      。这将在UI线程上运行,并避免任何线程问题,但是,由于
      UpdateTicker()
      代码也在此线程上运行,您的UI在处理时将无响应。这可能是个问题,也可能不是问题;如果您只需对字段/属性进行几次更改,就可以了
    • 在计时器回调中,使用
      this.Dispatcher.Invoke()
      this.Dispatcher.BeginInvoke()
      在其他处理完成后调用UI更新方法(例如:
      this.Dispatcher.Invoke((操作)updatecker)
      ),这将“通气”调用适当的线程进行UI更新,同时保持数据处理的异步性。换句话说,这是一种更有效的方法
  • TimeSpan
    结构有一个接受格式化的
    ToString()
    方法;或者,如果不方便,它有几个助手属性(
    Days
    Hours
    Minutes
    TotalDays
    TotalHours
    TotalMinutes
    等)可用于显示目的的

  • 你可以这样做: 在主窗口中:

    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();
            }
    
        }
    
    你可以这样做: 在主窗口中:

    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();
            }
    
        }
    

    谢谢你,你的回答很有效。用户界面正在按预期更新。谢谢你,你的回答很有效。用户界面正在按预期更新。