473,320 Members | 2,012 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

timer not being called when OnPaint calls Invalidate

Hi,

I have a simple form that handles all its paint functionality like so:

this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.Opaque, true);

And the entry point to this program is like so:
static void Main()
{
using (WorldMap frm = new WorldMap())
{
frm.Show();
Application.Run(frm);
}
}

I have my onpaint function call Invalidate at the very end like so:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Draw everything...
this.Invalidate()
}

The problem is that I want to setup a System.Windows.Forms.Timer when I
click the a certain key for instance. When I hit the key, I create a
timer successfully, but the timer's event never gets called after I
start the timer.. It seems the "this.Invalidate" within OnPaint does
not give the form the opportunity to call the tick-event of the timer.

I believe the problem (and ultimately the solution) lies in the fact
that the form is a single-threaded application. In my own ignorance, I
thought that when you create a timer it creates a new thread,
independent of the message loop of the application, but apparently not.

I also believe the solution lies in using Control.Invoke somewhere,
and/or possibly modifying Main so that when i create a Timer it can
operate independently of the OnPaint loop.

I'm soooo close to the solution that I can almost taste it, but I'm
stuck. The words, "Multithreaded app" screams at me. Any help would
be greatly appreciated. (I've looked at various topics in google, and
they too point to "Control.Invoke", but they were not clear on how it
would apply to my particular situation...)

Thanks in Advance,
-alex-

Nov 28 '06 #1
4 4582
What is it you would like to do when the timer expires?

On Nov 28, 12:48 pm, "grayaii" <gray...@gmail.comwrote:
Hi,

I have a simple form that handles all its paint functionality like so:

this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.Opaque, true);

And the entry point to this program is like so:
static void Main()
{
using (WorldMap frm = new WorldMap())
{
frm.Show();
Application.Run(frm);
}
}

I have my onpaint function call Invalidate at the very end like so:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Draw everything...
this.Invalidate()

}The problem is that I want to setup a System.Windows.Forms.Timer when I
click the a certain key for instance. When I hit the key, I create a
timer successfully, but the timer's event never gets called after I
start the timer.. It seems the "this.Invalidate" within OnPaint does
not give the form the opportunity to call the tick-event of the timer.

I believe the problem (and ultimately the solution) lies in the fact
that the form is a single-threaded application. In my own ignorance, I
thought that when you create a timer it creates a new thread,
independent of the message loop of the application, but apparently not.

I also believe the solution lies in using Control.Invoke somewhere,
and/or possibly modifying Main so that when i create a Timer it can
operate independently of the OnPaint loop.

I'm soooo close to the solution that I can almost taste it, but I'm
stuck. The words, "Multithreaded app" screams at me. Any help would
be greatly appreciated. (I've looked at various topics in google, and
they too point to "Control.Invoke", but they were not clear on how it
would apply to my particular situation...)

Thanks in Advance,
-alex-
Nov 28 '06 #2
"grayaii" <gr*****@gmail.comwrote in message
news:11**********************@j72g2000cwa.googlegr oups.com...
[...]
I have my onpaint function call Invalidate at the very end like so:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Draw everything...
this.Invalidate()
}
Let me make sure I understand what you are saying:

In your overridden "OnPaint" handler, which is called in response to a paint
event, which occurs any time an area of the control (form) is invalidated,
you invalidate the entire control (form)?

Seems to me you've got a pretty classic infinite loop there.
The problem is that I want to setup a System.Windows.Forms.Timer when I
click the a certain key for instance. When I hit the key, I create a
timer successfully, but the timer's event never gets called after I
start the timer.. It seems the "this.Invalidate" within OnPaint does
not give the form the opportunity to call the tick-event of the timer.
Sure, I can believe that. If you fill your message queue with paint events,
I can see how you might never get to a point where you can pull out the
timer event.
I believe the problem (and ultimately the solution) lies in the fact
that the form is a single-threaded application. In my own ignorance, I
thought that when you create a timer it creates a new thread,
independent of the message loop of the application, but apparently not.
Not using the Forms.Timer method of creating a timer, no. All that does is
use the usual Windows WM_TIMER mechanism, which relies on being able to get
a WM_TIMER message from the window's message queue.

If you want a timer that operates independently of the message queue, you
should probably use something like Threading.Timer instead. That will use a
different thread to handle the timer. Of course, if your timer code has to
interact with the window, you will wind up having to use Invoke or
BeginInvoke, which introduces a dependency on the message queue again.
Otherwise, it should be fine though.

IMHO, however, you should rethink the wisdom of calling Invalidate from
within your OnPaint handler. This is very bad form. Generally speaking,
you should only ever call Invalidate when there has actually been some
change in the data being drawn. It makes no sense to call Invalidate when
what you've just done is to have redrawn the control. Nothing about drawing
the control should be changing the way it should draw next time, and so
there should be no reason to call Invalidate from within the OnPaint
handler.
[...]
I'm soooo close to the solution that I can almost taste it, but I'm
stuck. The words, "Multithreaded app" screams at me.
You may or may not need to introduce a new thread into your program. That
depends on exactly what you're trying to do. However, personally I would
first attempt to solve your problem simply by removing the Invalidate from
the OnPaint handler. If your timer does something that requires the control
(form) to be redrawn, then call Invalidate there (assuming you have to...in
most simple cases, a form contains only controls that already know to
invalidate themselves if their data changes...simply setting the control's
data is enough).

Pete
Nov 28 '06 #3
Hi Peter,

Thank you so much for the response. I will try using Threading.Timer
first thing tomorrow morning.
My application is actually a game, which is why OnPaint has to be
called so often.

There are so many topics on the "ultimate c# game loop", but "they" say
the easiest way to have a high frame rate game is to call Invalidate at
the end of OnPaint (for instance,
http://geekswithblogs.net/jolson/articles/2173.aspx), but you are
right; I'm flooding the message cue with OnPaint which is why my poor
timer never gets the message. When I read the various topics on how to
do the "game loop", the words "infinite loop" also set alarms off in my
head :) I'll do some more research on this subject to see if I can get
around this.

Interestingly enough the game works pretty well, in the sense that
MouseEvents and KeyEvents get trapped fine and all the animations are
drawn rather nicely. Why those events work and not the timer is beyond
my understanding :) What I do believe is that I have is the classic
problem of distributing the workload to the appropriate places. For
example, my OnPaint function is currently doing way too much work: For
instance, the game lags significantly when a monster is performing a
"Shortest Path Algorithm" call on a target that is very far away, and
it's because that function call is inside OnPaint. The more OnPaint
does, the lower the framerate.

Argh. Your response only strengthens my resolve to re-think how my game
should be structured :)

Thank you again for you response!
-alex-

On Nov 27, 9:06 pm, "Peter Duniho" <NpOeStPe...@NnOwSlPiAnMk.com>
wrote:
"grayaii" <gray...@gmail.comwrote in messagenews:11**********************@j72g2000cwa.g ooglegroups.com...
[...]
I have my onpaint function call Invalidate at the very end like so:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Draw everything...
this.Invalidate()
}Let me make sure I understand what you are saying:

In your overridden "OnPaint" handler, which is called in response to a paint
event, which occurs any time an area of the control (form) is invalidated,
you invalidate the entire control (form)?

Seems to me you've got a pretty classic infinite loop there.
The problem is that I want to setup a System.Windows.Forms.Timer when I
click the a certain key for instance. When I hit the key, I create a
timer successfully, but the timer's event never gets called after I
start the timer.. It seems the "this.Invalidate" within OnPaint does
not give the form the opportunity to call the tick-event of the timer.Sure, I can believe that. If you fill your message queue with paint events,
I can see how you might never get to a point where you can pull out the
timer event.
I believe the problem (and ultimately the solution) lies in the fact
that the form is a single-threaded application. In my own ignorance, I
thought that when you create a timer it creates a new thread,
independent of the message loop of the application, but apparently not.Not using the Forms.Timer method of creating a timer, no. All that does is
use the usual Windows WM_TIMER mechanism, which relies on being able to get
a WM_TIMER message from the window's message queue.

If you want a timer that operates independently of the message queue, you
should probably use something like Threading.Timer instead. That will use a
different thread to handle the timer. Of course, if your timer code has to
interact with the window, you will wind up having to use Invoke or
BeginInvoke, which introduces a dependency on the message queue again.
Otherwise, it should be fine though.

IMHO, however, you should rethink the wisdom of calling Invalidate from
within your OnPaint handler. This is very bad form. Generally speaking,
you should only ever call Invalidate when there has actually been some
change in the data being drawn. It makes no sense to call Invalidate when
what you've just done is to have redrawn the control. Nothing about drawing
the control should be changing the way it should draw next time, and so
there should be no reason to call Invalidate from within the OnPaint
handler.
[...]
I'm soooo close to the solution that I can almost taste it, but I'm
stuck. The words, "Multithreaded app" screams at me.You may or may not need to introduce a new thread into your program. That
depends on exactly what you're trying to do. However, personally I would
first attempt to solve your problem simply by removing the Invalidate from
the OnPaint handler. If your timer does something that requires the control
(form) to be redrawn, then call Invalidate there (assuming you have to...in
most simple cases, a form contains only controls that already know to
invalidate themselves if their data changes...simply setting the control's
data is enough).

Pete
Nov 28 '06 #4
"grayaii" <gr*****@gmail.comwrote in message
news:11**********************@n67g2000cwd.googlegr oups.com...
Thank you so much for the response. I will try using Threading.Timer
first thing tomorrow morning.
Okay. Hopefully you will wind up not needing it, but at least it will be
useful to know if it's a decent alternative for you.
My application is actually a game, which is why OnPaint has to be
called so often.

There are so many topics on the "ultimate c# game loop", but "they" say
the easiest way to have a high frame rate game is to call Invalidate at
the end of OnPaint
Easy is not necessary right. In fact, it almost never is. :)

You don't say what kind of game you have, but it may or may not be the case
that you need the highest frame rate. However, typically a game that
requires a high frame rate will not use the normal window messaging paradigm
at all. It will redraw its graphics via some other mechanism. Going
through User (the window message pump) and GDI (the invalidation/redraw
paradigm) is overhead that isn't acceptable when frame rate needs to be
maximized.

In addition, for many games the worst thing you can do with respect to frame
rate is to invalidate the entire window. Doing so (or the equivalent) makes
a lot of sense for 3D games where the camera is always in motion, because
the entire view is going to have to be redrawn every frame anyway. You
wouldn't waste time invalidating and waiting for a WM_PAINT message (which
is what underlies the .NET paint event mechanism). You'd just draw a new
frame as often as you could.

But if you don't have a 3D game, you're dealing with some sort of 2D
sprite-like graphics, most likely. In this case, often only small areas of
the screen are actually going to change at any given time. Even if you have
(for example) a grid playfield in which every other square contains an
animated icon, a) only half the window actually needs to be redrawn, and b)
it's unlikely you actually have icon animations rendered at as high a frame
rate as is typically possible (for example, animations may be stored at 15
or 30 frames per second, while the actual possible frame rate might be over
100 fps, especially for 2D stuff...that means most of the time, even the
animated parts of your screen aren't changing from one frame to the next).

As a general rule, simple 2D games work well simply by using the normal
Windows redraw mechanisms. Based on user input and game mechanics,
invalidate only the portion of the window that has actually changed, when it
changes, and let the paint event redraw it next chance it gets. Don't force
the entire window to be redrawn, because there's no point if nothing has
changed.

(And I should note here: typically, it's not so much that you avoid running
code to redraw a portion of the screen, as it is that the code that does run
winds up not having to actually move any data around, because drawing is
clipped to exclude any portion that hasn't been invalidated. It is
theoretically possible to inspect the clipping area in the OnPaint method,
and not draw stuff that's entirely outside the clipped area. But the
biggest gains comes simply from the clipping itself; unless you have very
simple, fast ways to avoid drawing altogether, it's faster to just run
through all of the drawing code normally, and let Windows save you the time
of actually drawing anything when it's outside the clipped area).
[...]
Interestingly enough the game works pretty well, in the sense that
MouseEvents and KeyEvents get trapped fine and all the animations are
drawn rather nicely. Why those events work and not the timer is beyond
my understanding :)
Probably because the message queue isn't strictly FIFO. Windows does some
prioritization on messages in the queue, allowing higher priority ones to
bypass lower priority ones.

I don't know the specifics off the top of my head, but it wouldn't surprise
me to find that the WM_TIMER message is low on the priority list, behind
WM_PAINT, WM_MOUSE, and WM_KEYDOWN/UP.
What I do believe is that I have is the classic
problem of distributing the workload to the appropriate places. For
example, my OnPaint function is currently doing way too much work: For
instance, the game lags significantly when a monster is performing a
"Shortest Path Algorithm" call on a target that is very far away, and
it's because that function call is inside OnPaint. The more OnPaint
does, the lower the framerate.
Well, one problem is that your OnPaint method should *only* be painting.
Game mechanics should be somewhere else. The exact mechanism for your game
mechanics will vary according to your needs, but it's a mistake to
intertwine the game mechanics with the game rendering. The two are
completely different things, and while they have to interact with each
other, they should remain isolated from each other in terms of where the
code is.

Another problem may be that your path finding algorithm is either flawed or
inappropriate. Even after you seperate drawing from calculating, your
monster can't go anywhere until it's calculated a path to travel. If you
insist on holding up the rest of the game until the monster's decided where
to go, then frame rate will suffer regardless. Not because you can't draw
that fast, but just because nothing's happened until the monster figures out
where to go.

Alternatives to that problem include:

* Multi-threading the game AI. This would allow your monster to think
on a different thread than that used for the rest of the game. Just as the
game can proceed without the human player doing anything (while the human
tries to figure out what to do), it can proceed without the monster doing
anything while it tries to figure out what to do. Using this method, the
monster could interact with the game engine in much the same way the user
does: there would be some sort of "monster input" API that the monster AI
could call to tell the game engine where and how it wants to move, once it
figures that out.

* Use a more efficient path-finding algorithm. This may or may not be a
possibility, depending on how you've done the algorithm at this point.
However, it's probably worth a look. Especially if the routine is used on a
regular basis, it behooves you to make sure it is absolutely as efficient as
possible.

* Pare the algorithm down so that it doesn't do so much work each frame.
This is especially appropriate if the monster will have to recalculate the
path the next frame anyway (in response, for example, to player input).
Don't waste time calculating the optimal path, since you're going to toss
out most of that work the next frame anyway...just do enough of the
calculation to get the monster moving in the right direction. Do that again
next frame. Keep doing that until the monster reaches its goal. Unless
your monster can move through the entire optimal path in the time it takes
to draw a single frame, then calculating that entire optimal path each frame
is a waste of time.

* You could even combine the first and third ideas above, by allowing
your algorithm to maintain state somewhere and tracking how much time it's
spent. Set an upper time limit, and have it save its state and yield
control to the rest of the program when it hits that upper time limit. This
sort of multi-threads the monster AI without actually putting it on another
thread. Personally, I think this would be overkill (if you really wanted
this approach, just do it via threading), but it's certainly a possibility.

In any case, there's just no reason to call Invalidate from within the
OnPaint method. If you need the absolute maximum rate with respect to
redrawing, just call the drawing code as often as you can without even
bothering to use Invalidate. Otherwise, use the Windows redraw mechanism as
it was designed, by calling Invalidate only to invalidate areas that you
know have actually changed, when those areas have actually changed (in
response to user input, game mechanics, and the like). And don't forget,
depending on what you're actually drawing, you may find that invalidating
the entire window is actually *slower* than invalidating smaller areas as
necessary.

So, to sum up:

* Don't do game AI when you ought to be drawing.
* Don't draw stuff when you ought to be doing game AI.
* Be smarter/more efficient about monster AI. Don't calculate things
that you will never use.
* Don't call Invalidate from within the OnPaint method.
* If you have to repaint the window as frequently as possible, just
repaint it every chance you get. Don't waste time messing around with the
Invalidate/paint-event cycle.
* Keep in mind that depending on your game, you may not have to repaint
the entire window at all. If that's the case, then *do* use the
Invalidate/paint-event cycle and don't invalidate the entire window unless
you know for sure the entire window has to be redrawn.
Argh. Your response only strengthens my resolve to re-think how my game
should be structured :)

Thank you again for you response!
You're welcome. Hope the above is also helpful.

Pete
Nov 28 '06 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

11
by: Sagaert Johan | last post by:
I have made a custom control that draws a rectangle when the mouse is down, and does nothing when the mouse is up. I set/reset a flag in MouseDown/Mouse up and use this to do the drawing in the...
3
by: Andy | last post by:
Hello. I want to use System.Threading.Timer to control when my windows form is invalidated. I want it to update at 60 frames per second. But, if I set the Interval of my timer to anything less than...
6
by: Dan | last post by:
I've created a pocketpc app which has a startup form containing a listview. The form creates an object which in turn creates a System.Threading.Timer. It keeps track of the Timer state using a...
10
by: pcnerd | last post by:
I've created a demo program that draws random lines on the form. At the end, a messagebox pops up with the words"I'm done!". When I click on "OK", the messagebox disappears, but a white square is...
1
by: sean | last post by:
I'm trying to create "rubber-band" rectangles by overriding the OnPaint method to place rectangles on top of all graphic controls, but when I call Me.Invalidate() (when the user moves the mouse),...
7
by: =?Utf-8?B?Um9oaXQ=?= | last post by:
I have a timer object that calls UpdateWindow(). The WM_TIMER message is processed in the main Win32 message loop for the window. However, when I run the app, the image doesn't get updated (there...
7
by: ssecorp | last post by:
I am not clear about the results here. from timeit import Timer import Decorators def fib(n): a, b = 1, 0 while n: a, b, n = b, a+b, n-1
14
by: raylopez99 | last post by:
KeyDown won't work KeyPress fails KeyDown not seen inspired by a poster here:http://tinyurl.com/62d97l I found some interesting stuff, which I reproduce below for newbies like me. The main...
8
by: Rainer Queck | last post by:
Hello NG, in my application I have a Panel, docked right to my Form. On this panel I draw - cyclic triggered by a System.Winforms.Timer - a bitmap using the Graphics class. using (Graphics g =...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
0
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.