C# 更新计时器上的主窗口。已用事件
我正在编写一个家庭WPF应用程序,它以配置的间隔从服务器获取文件 这是一个基本窗口,带有两个标签。我有以下几点C# 更新计时器上的主窗口。已用事件,c#,wpf,C#,Wpf,我正在编写一个家庭WPF应用程序,它以配置的间隔从服务器获取文件 这是一个基本窗口,带有两个标签。我有以下几点 开始时间(反映发生“开始”事件的日期时间 持续时间(反映应用程序已运行的时间) 速度(文件的下载速度) 我想每秒更新主窗口上的持续时间,因此我有以下代码来完成这项工作(在单独的类“RunDownloader.cs”中) 在_ticktimer_已过时,我在主窗口_mainWindow.UpdateTicker()中调用一个方法 这将执行以下操作 public void Up
- 开始时间(反映发生“开始”事件的日期时间
- 持续时间(反映应用程序已运行的时间)
- 速度(文件的下载速度)
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 =
}
我有两个问题
提前感谢您的回答。: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线程”)。这里有两个主要选项:
- 使用
。这将在UI线程上运行,并避免任何线程问题,但是,由于dispatchermer
代码也在此线程上运行,您的UI在处理时将无响应。这可能是个问题,也可能不是问题;如果您只需对字段/属性进行几次更改,就可以了UpdateTicker()
- 在计时器回调中,使用
或this.Dispatcher.Invoke()
在其他处理完成后调用UI更新方法(例如:this.Dispatcher.BeginInvoke()
),这将“通气”调用适当的线程进行UI更新,同时保持数据处理的异步性。换句话说,这是一种更有效的方法this.Dispatcher.Invoke((操作)updatecker)
TimeSpan
结构有一个接受格式化的ToString()
方法;或者,如果不方便,它有几个助手属性(Days
,Hours
,Minutes
,TotalDays
,TotalHours
,TotalMinutes
等)可用于显示目的的UpdateTicker()
方法。但是,大多数UI框架(包括WPF)禁止从创建相应控件的线程以外的线程访问UI控件(后者通常表示为“UI线程”)。这里有两个主要选项:
- 使用
。这将在UI线程上运行,并避免任何线程问题,但是,由于dispatchermer
代码也在此线程上运行,您的UI在处理时将无响应。这可能是个问题,也可能不是问题;如果您只需对字段/属性进行几次更改,就可以了UpdateTicker()
- 在计时器回调中,使用
或this.Dispatcher.Invoke()
在其他处理完成后调用UI更新方法(例如:this.Dispatcher.BeginInvoke()
),这将“通气”调用适当的线程进行UI更新,同时保持数据处理的异步性。换句话说,这是一种更有效的方法this.Dispatcher.Invoke((操作)updatecker)
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();
}
}
谢谢你,你的回答很有效。用户界面正在按预期更新。谢谢你,你的回答很有效。用户界面正在按预期更新。