Search code examples
c#.nettimergdionpaint

Timer problem with GDI+


I am currently stuck at a really strange problem with GDI and timers.

First the Code:

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
  }
}

I am trying here to move a painted graph in a specific time to 1 "graph unit" to the left. So i use a timer which will change the offset in little steps, so it will be a smooth moving (thats the refreshRate for).

At the first view this code worked, but later i found following problem: If i am using a refreshRate of 1 (1Hz) it will just fine move my graph in 1 step 1 (graph unit) to the left. If i am increasing the refreshRate my movement will slow done. At 20 FPS its slighly slow, at 200 FPS its really slow..

So here is what i tried:

  1. I used Refresh or Update instead of Invalidate

  2. I used a normal Thread (with Sleep) instead of the timer

Both code changes didnt changed the result..

Beside the movement with the timer I also can move the graph with my mouse and if the timer is running i can still smoothly move the graph with my mouse. So its not a peformance problem..

I thought of a problem in the painting queue, because I am refreshing faster than the painting is done? (But why can I sill move the graph smoothly with my mouse?!)

So i need a little help here. Thanks


Solution

  • You may try to change the problem slightly.

    Since you do not know how long your painting will take, you cannot make an assumption about that.

    What you do know, is the amount of time you want the transition to happen, let's say 30 seconds, something like this:

    private MAX_TRANSITION_TIME = 30; //seconds
    

    At this point you can also track the amount of time it has elapsed since the start of your operation, by recording when your operation has started, let's say

    private DateTime _startMoment;
    

    Now what you can do is to make sure you have the right routine at hand and you calculate your position based on the difference between the startMoment and now

    var elapsedTime = DateTime.Now.Subtract(_startMoment).Milliseconds;
    var elapsedPercent = elapsedTime / MAX_TRANSITION_TIME * 1000.0 ;
    

    From now on what you can do is to paint accordingly to your percent of time elapsed.

    So after your OnPaint is done you should be able to refresh it. If you use one UI timer, you can do this:

     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
         }
     }
    

    Where Timer1 has to be the Timer control.

    In the Interval event you just write

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

    Hope this helps