我使用 C# 和 .NET 4.0 创建了一个新的 Windows 窗体应用程序,并且我有一个必须每 60 秒自动调用一次的函数。我的问题是我不知道在哪里调用这个函数。.NET 表单似乎没有内置的更新事件。
我将如何让这个函数每 60 秒调用一次?对不起,如果这是一个初学者的问题。
您可以设置一个计时器以每 16 毫秒(即 1/60 秒)调用一次回调。
@spender 提到的重要一点:如果您需要非常精确的计时,例如精确每 1/60 秒的精度,您将不会对此解决方案感到满意。Windows 本身并不能很好地在这种情况下进行高分辨率计时。向@spender 提出建议以供提及。
这是一个示例类,粗略地描述了它在普通香草类中的外观,因此您需要将其调整为您的表单:
您可能还想在后台线程上调用它,但如果您是 WinForms 的新手,我们将从小块开始。让我们先试试计时器,然后从那里开始。
class Demo{
System.Timers.Timer myTimer;
void InitializeTimer(){
myTimer = new Timer(16); // elaps every 1/60 sec , appx 16 ms.
myTimer.ElapsedEventHandler+=new ElapsedEventHandler(myTimerEventHandler); //define a handler
myTimer.Enabled=true; //enable the timer.
}
void myTimerEventHandler(object sender, ElapsedEventArgs e){
// do your thing here
}
}
编辑:后台线程创建和基于调用的 GUI 更新的额外演示代码
正如我在下面的评论中所指出的,这并没有我想要的那么完美,但我认为它说明了要点。它定义了一个 BackgroundWorker 线程来将线程调用移动到后台;线程回调检查是否需要调用 Invoke,并通过委托调用直接回调自身,以允许在“if (InvokeRequired)”语句的“else”块中更新自定义表单。简而言之,一个后台线程启动,并启动一个定时器;当计时器到时,它调用后台线程上的更新程序,它检查是否必须调用 Invoke,如果是,则通过调用方法执行线程上下文切换回 GUI 线程,然后执行图形用户界面更新。将您的自定义更新代码放在那个“其他”块中。我希望这有帮助!!!
public partial class Form1 : Form
{
delegate void FormUpdateDelegate(object sender, ElapsedEventArgs e);
public BackgroundWorker backgroundThread;
System.Timers.Timer foo;
Random colorgen = new Random();
public Form1()
{
InitializeComponent();
backgroundThread = new BackgroundWorker();
backgroundThread.DoWork+=new DoWorkEventHandler(backgroundThread_DoWork);
backgroundThread.RunWorkerAsync();
}
public void formUpdater(object sender, ElapsedEventArgs e)
{
if (InvokeRequired)
{
FormUpdateDelegate d = new FormUpdateDelegate(formUpdater);
Invoke(d, new object[] { sender, e });
}
else
{
// Do your form update here
this.label1.ForeColor = Color.FromArgb(colorgen.Next());
}
}
public void backgroundThread_DoWork(object sender, DoWorkEventArgs e)
{
foo = new System.Timers.Timer(16);
foo.Elapsed += new ElapsedEventHandler(formUpdater);
foo.Start();
}
}
我只是在控制台应用程序中设置它,它每秒运行良好:
var timer = new System.Timers.Timer(1000);
timer.Enabled = true;
timer.Elapsed += new ElapsedEventHandler(delegate(object sender, ElapsedEventArgs eventArgs)
{
Console.WriteLine("Elapsed");
});
当您至少需要20
每秒精确次时,寄希望于系统计时器是一种不好的做法。
我建议使用像这样的循环
Timespan timePerFrame = Timespan.FromMilliseconds(16);
while (_isRunning)
{
Stopwatch timer = Stopwatch.StartNew()
// Action.
while (timer.ElapsedMilliseconds < timePerFrame) { /* Nothing? */ }
}
如果您的系统支持高精度秒表,这将为您提供完整的精度。(.IsHighResolution
场)。