GDI +的定时器问题

时间:2011-09-21 20:23:59

标签: c# .net timer gdi onpaint

我目前在GDI和计时器方面遇到了一个非常奇怪的问题。

首先是守则:

class Graph : UserControl {
  private System.Threading.Timer timer;
  private int refreshRate = 25;              //Hz (redrawings per second)
  private float offsetX = 0;                 //X offset for moving graph

  public Graph() {
    timer = new System.Threading.Timer(timerTick);
  }

  private void timerTick(object data) {
    offsetX -= 1 / refreshRate;
    this.Invalidate();
  }

  public void timerStart() {
    timer.Change(0, 1000 / refreshRate);
  }

  private void onPaint(object sender, PaintEventArgs e) {
    //350 lines of code for drawing the graph
    //Here the offsetX will be used to move the graph
  }
}

我正在尝试将特定时间内的绘制图形移动到左侧的1“图形单位”。所以我使用一个计时器,它会以很小的步长改变偏移,所以它将是一个平滑的移动(这就是refreshRate)。

在第一个视图中,此代码有效,但后来我发现了以下问题: 如果我使用的刷新率为1(1Hz),则可以将我的图形在1步(图形单位)中向左移动。如果我增加refreshRate,我的动作会慢下来。在20 FPS时,它的速度非常慢,速度为200 FPS,速度非常慢......

所以这就是我的尝试:

  1. 我使用了Refresh或Update而不是Invalidate

  2. 我使用普通线程(带睡眠)而不是计时器

  3. 两个代码更改都没有改变结果..

    除了使用计时器的移动之外,我还可以使用鼠标移动图形,如果计时器正在运行,我仍然可以使用鼠标平滑地移动图形。所以它不是一个性能问题..

    我想到了绘画队列中的一个问题,因为我比绘画更快地刷新了吗? (但为什么我可以用鼠标顺利移动图形?!)

    所以我需要一点帮助。 感谢

2 个答案:

答案 0 :(得分:1)

  

速度为20 FPS时速度很慢,速度为200 FPS,速度非常慢......

这里有一个根本问题;要获得200fps的刷新率,您需要每5ms重绘一次。这绝不会发生。无论您将计时器的间隔设置为何,其分辨率都限制在10-15ms左右。所以你最好的帧速率大约是66-100fps,这假设你的绘图代码没有时间,当然它没有。

最重要的是,你正在使用System.Threading.Timer 在UI线程上执行它的回调,所以从那里调用Invalidate()可能甚至都不安全。您通常不会将System.Threading.Timer用于UI代码。

您可能想尝试使用所谓的Multi-Media Timer shown here,它使用CPU的高性能计时器,并提供大约1毫秒的分辨率。

答案 1 :(得分:0)

您可以尝试稍微更改问题。

既然你不知道你的画会花多长时间,你就不能对此作出假设。

你知道的是你希望过渡发生的时间,比方说30秒,就像这样:

private MAX_TRANSITION_TIME = 30; //seconds

此时,您还可以跟踪自操作开始以来经过的时间,记录您的操作何时开始,比如说

private DateTime _startMoment;

现在您可以做的是确保您拥有正确的例行程序,并根据startMoment和现在

之间的差异来计算您的位置
var elapsedTime = DateTime.Now.Subtract(_startMoment).Milliseconds;
var elapsedPercent = elapsedTime / MAX_TRANSITION_TIME * 1000.0 ;

从现在开始,您可以做的是根据您经过的时间百分比进行绘画。

因此,在完成OnPaint后,您应该可以刷新它。如果您使用一个UI计时器,则可以执行以下操作:

 private void onPaint(object sender, PaintEventArgs e) {
       Timer1.Enabled = false;

     //350 lines of (adjusted)code go here

     If (ElapsedPercent<1)
     {
         Timer1.Enabled=True;
     }
     else
     {
         // you may need to perform the last draw setting the ElapsedPercent 
         // to 1. This is because it is very unlikely that your 
         // last call will happen at the very last millisecond
     }
 }

其中Timer1必须是Timer控件。

在Interval事件中,您只需编写

 _startMoment = DateTime.Now();
 this.Invalidate();

希望这有帮助