By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,847 Members | 2,392 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,847 IT Pros & Developers. It's quick & easy.

Graphics ... almost working ... missing one line?

P: n/a
I previously asked about two problems I had with some graphics - the first
was that when I drew animation to a picturebox it wouldn't display when the
Form loaded.

It was suggested to me by everyone that I stop using my own CreateGraphics
calls, and override an OnPaint call to do this. As suggested, I created a
custom control "drawBox" with an override on its OnPaint. Because I am using
manual double buffering, as people suggested it was really just a matter of
putting myBuffer.Render() into the OnPaint, and using drawBox.Invalidate as
the call. In the end I only had to change/add about 8 lines of code, and its
only taken about 16 hours to work out exactly which ones.

However, there is one annoying problem that remains. I can get it to show
the initial screen just fine when it loads. But what I really want it to do
is play the opening animation.

If I put the call to animate the screen

play_animation();

As the very last line of Form1_Load or anywhere else that I have tried, then
the Form1 doesn't actually load and show anything until the animation is
finished, at which point the Form pops up showing only the final (end) frame
of the animation.

I had the same problem with the old (bad) way I did graphics, so I assume
its not related ... but FWIW, here are they key parts of my new graphics:

public partial class Form1 : Form
{
static public drawBox pictureBoxn;

....

public class drawBox : Control
{
public drawBox()
{
this.Height = 600;
this.Width = 600;
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
myBuffer.Render();

}
}

I call the following in Form1_Load:

pictureBoxn = new drawBox();
currentContext = BufferedGraphicsManager.Current;
myBuffer =
currentContext.Allocate(pictureBoxn.CreateGraphics (),
pictureBoxn.DisplayRectangle);
this.Controls.Add(pictureBoxn);

canvas = myBuffer.Graphics;
canvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// canvas.Clear(Color.White);
screenwidth = pictureBoxn.Width;
screenheight = pictureBoxn.Height;
tracks = new Bitmap((int)screenwidth, (int)screenheight);
backgroundcanvas = Graphics.FromImage(tracks);
backgroundcanvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
backgroundcanvas.Clear(Color.Red);
pictureBoxn.Paint += new
System.Windows.Forms.PaintEventHandler(this.pictur eBoxn_Paint);

pictureBoxn.Refresh();
pictureBoxn.Show();
pictureBoxn.Update();
pictureBoxn.Invalidate();
Dec 8 '07 #1
Share this Question
Share on Google+
11 Replies


P: n/a
Peter,

What is in the code for play_animation? Are you looping through and
repainting on every invocation of the loop? It seems like that is the case
here.

What you really want to do is create a timer which you invalidate your
picturebox on every tick (the frame rate of your animation will depend on
the frequency of the timer).
--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"Peter Webb" <we********@DIESPAMDIEoptusnet.com.auwrote in message
news:47***********************@news.optusnet.com.a u...
>I previously asked about two problems I had with some graphics - the first
was that when I drew animation to a picturebox it wouldn't display when the
Form loaded.

It was suggested to me by everyone that I stop using my own CreateGraphics
calls, and override an OnPaint call to do this. As suggested, I created a
custom control "drawBox" with an override on its OnPaint. Because I am
using manual double buffering, as people suggested it was really just a
matter of putting myBuffer.Render() into the OnPaint, and using
drawBox.Invalidate as the call. In the end I only had to change/add about
8 lines of code, and its only taken about 16 hours to work out exactly
which ones.

However, there is one annoying problem that remains. I can get it to show
the initial screen just fine when it loads. But what I really want it to
do is play the opening animation.

If I put the call to animate the screen

play_animation();

As the very last line of Form1_Load or anywhere else that I have tried,
then the Form1 doesn't actually load and show anything until the animation
is finished, at which point the Form pops up showing only the final (end)
frame of the animation.

I had the same problem with the old (bad) way I did graphics, so I assume
its not related ... but FWIW, here are they key parts of my new graphics:

public partial class Form1 : Form
{
static public drawBox pictureBoxn;

...

public class drawBox : Control
{
public drawBox()
{
this.Height = 600;
this.Width = 600;
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
myBuffer.Render();

}
}

I call the following in Form1_Load:

pictureBoxn = new drawBox();
currentContext = BufferedGraphicsManager.Current;
myBuffer =
currentContext.Allocate(pictureBoxn.CreateGraphics (),
pictureBoxn.DisplayRectangle);
this.Controls.Add(pictureBoxn);

canvas = myBuffer.Graphics;
canvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// canvas.Clear(Color.White);
screenwidth = pictureBoxn.Width;
screenheight = pictureBoxn.Height;
tracks = new Bitmap((int)screenwidth, (int)screenheight);
backgroundcanvas = Graphics.FromImage(tracks);
backgroundcanvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
backgroundcanvas.Clear(Color.Red);
pictureBoxn.Paint += new
System.Windows.Forms.PaintEventHandler(this.pictur eBoxn_Paint);

pictureBoxn.Refresh();
pictureBoxn.Show();
pictureBoxn.Update();
pictureBoxn.Invalidate();

Dec 8 '07 #2

P: n/a

"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.comwrote in
message news:39**********************************@microsof t.com...
Peter,

What is in the code for play_animation? Are you looping through and
repainting on every invocation of the loop? It seems like that is the
case here.

What you really want to do is create a timer which you invalidate your
picturebox on every tick (the frame rate of your animation will depend on
the frequency of the timer).
Yes. I am looping through dozens of on-screen objects, updating their
position. That's what my program does, and spends all its time. In the main
animation loop, I still use a myBuffer.Render() command, as the
drawBox.Invalidate() command flickers (even though it just does a
myBuffer.Render() ).

I was told before (and I think by you) to use a timer in asynchronous
thread. It didn't seem an issue in my app, as most people will want to run
it flat out, so having two threads was more complexity that I didn't
understand.

The annoying thing is that it is just a problem at start up of the Form.
Everything else works brilliantly.

Is there some quick and dirty I can do for the start-up to make the form
appear before the animation starts (or even finishes!)?

Or if I have to do this properly, can you refer me to a web page that might
get me started?
Dec 8 '07 #3

P: n/a
On Sat, 08 Dec 2007 08:11:47 -0800, Peter Webb
<we********@DIESPAMDIEoptusnet.com.auwrote:
Yes. I am looping through dozens of on-screen objects, updating their
position.
Don't do that.
That's what my program does, and spends all its time.
Even so, don't do that.
In the main animation loop, I still use a myBuffer.Render() command, as
the drawBox.Invalidate() command flickers (even though it just does a
myBuffer.Render() ).
The flicker is because calling Invalidate() causes the entire control to
be erased first, before the OnPaint() method is called. You can avoid
this in a couple of ways, but the simplest in a custom control is to just
set the DoubleBuffered property of your control to true (note that this
property has nothing to do with your own buffering...it causes the OS to
create a new off-screen buffer to be used for drawing during updates to
the control).

Flicker isn't a good reason to abandon the standard Windows drawing
model. There are better ways to fix it.
I was told before (and I think by you) to use a timer in asynchronous
thread. It didn't seem an issue in my app, as most people will want to
run it flat out, so having two threads was more complexity that I didn't
understand.
Some points here:

* You might not need a different thread. It's possible that the
Forms.Timer class provides enough accuracy for your animation. It offers
a slightly simpler way to deal with timing.

* A second thread should not be all that complicated in this case.
The trickiest part is remembering that you need to use Control.Invoke() to
call any methods that directly use the UI component. In this case, that's
likely to just be the call to Invalidate(), which you can accomplish like
this (using an anonymous method):

myCustomControl.Invoke((MethodInvoker)delegate()
{ myCustomControl.Invalidate(); });

Note: when using a second thread, you _might_ still run into timing
issues, because while the thread itself might be able to manage its own
timing reasonably well, the redraw stuff (invalidation and redrawing after
invalidation) still has to go through the normal window message queue that
the Forms.Timer object would have had to. There are some basic
limitations when writing a regular Windows applications that will be
present no matter _how_ you do the drawing, right way or wrong way.

I would recommend you try for the Forms.Timer class first, as it provides
a nice, easy API where you don't have cross-thread issues. In addition to
the requirement to invalidate from the main GUI thread (and thus the need
to use Invoke()), using a second thread will also mean you need to impose
some sort of thread synchronization. Either you'll need to essentially
double-buffer your buffer, always having a finished copy used for handling
the control update and a second copy into which you actually draw the new
data, or you'll need to use lock() to protect any code using the buffer so
that you don't have two threads trying to manipulate the data structure at
the same time. Neither is difficult, but why introduce the complexities
if you don't have to?

Note that no matter what you do, you're going to be limited to something
like 15-20 fps using a mechanism like this. Trying to update any more
frequently than that is likely to lead to uneven animation, with some
frames being displayed much shorter or longer periods of time than
others. For faster animation than that, you simply need to get away from
the standard Windows application model completely, which you're not going
to do with a regular .NET forms application.

* It's not really true that most people "will want to run it flat
out". Practically every user I've met wants to be able to exit an
application whenever they want to. Using your design won't allow that, as
the form will not respond to user input until the animation has completed.
The annoying thing is that it is just a problem at start up of the Form.
Everything else works brilliantly.
I'm not sure I'd use the term "brilliantly" to describe how _everything
else_ in your application works. You may have gotten the animation to
animate, but other than that your application isn't a very well-behaved
Windows application.

As promised to you, by failing to adhere to the standard drawing model in
Windows, you've created new, difficult-to-solve problems.
Is there some quick and dirty I can do for the start-up to make the form
appear before the animation starts (or even finishes!)?
Well, one option would be to move your animation code to some place where
you know it will take place after the form has drawn. But keep in mind
that doing that will only change the specific scenarios in which your form
won't draw. It will be trivial to demonstrate other scenarios in which
the form needs to be draw _again_, but won't be able to because you're
stuck in your animation loop.

The correct fix, and the only one I will recommend, is to fix your code so
that your control _only_ draws itself in response to WM_PAINT messages.
In other words, in the OnPaint() method, and in there drawing only the
_current_ state of the data. Updates of the data used for animation
(which in your case basically just means your off-screen buffer) must be
done outside of the drawing code. When the data is updated, simply call
Invalidate() to cause a redraw to happen.
Or if I have to do this properly, can you refer me to a web page that
might get me started?
I posted a number of useful links in the previous thread, to pages on the
MSDN web site. I believe someone else has posted one or more links to Bob
Powell's web site as well (which I can't vouch for, but I've seen other
people recommend it highly).

Mostly though, you really need to just focus on the basics. There's a
very specific, but easily-described paradigm here:

* Make sure your drawing code responds correctly to OnPaint(),
rendering the current state of the data and being able to do so at any
time.
* Don't draw from the same place where you change the data. Call
Invalidate() instead.

From that, everything else derives.

Pete
Dec 8 '07 #4

P: n/a
>>Is there some quick and dirty I can do for the start-up to make the form
>>appear before the animation starts (or even finishes!)?
Yes. Try the FormShown event instead. The FormLoad event is NOT the place
to try animation.

"Peter Webb" <we********@DIESPAMDIEoptusnet.com.auwrote in message
news:47***********************@news.optusnet.com.a u...
>
"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.comwrote
in message news:39**********************************@microsof t.com...
>Peter,

What is in the code for play_animation? Are you looping through and
repainting on every invocation of the loop? It seems like that is the
case here.

What you really want to do is create a timer which you invalidate your
picturebox on every tick (the frame rate of your animation will depend on
the frequency of the timer).

Yes. I am looping through dozens of on-screen objects, updating their
position. That's what my program does, and spends all its time. In the
main animation loop, I still use a myBuffer.Render() command, as the
drawBox.Invalidate() command flickers (even though it just does a
myBuffer.Render() ).

I was told before (and I think by you) to use a timer in asynchronous
thread. It didn't seem an issue in my app, as most people will want to run
it flat out, so having two threads was more complexity that I didn't
understand.

The annoying thing is that it is just a problem at start up of the Form.
Everything else works brilliantly.

Is there some quick and dirty I can do for the start-up to make the form
appear before the animation starts (or even finishes!)?

Or if I have to do this properly, can you refer me to a web page that
might get me started?

Dec 8 '07 #5

P: n/a
On Sat, 08 Dec 2007 15:10:21 -0800, Joey Joe Joe <no@nothing.comwrote:
>>Is there some quick and dirty I can do for the start-up to make the
form appear before the animation starts (or even finishes!)?

Yes. Try the FormShown event instead. The FormLoad event is NOT the
place to try animation.
Neither is FormShown. _Starting_ some animation process might be
appropriate there, and would be just as appropriate in the FormLoad
event. Actually _doing_ the animation belongs there no more than it
belongs in FormLoad.
Dec 8 '07 #6

P: n/a
I never said that the FormShown event was the optimum place to do animation.
I was answering a specific question from the poster which is, "Is there some
quick and dirty I can do for the start-up to make the form appear before the
animation starts (or even finishes!)?" Did you notice the quick and dirty
reference? Hence, I offered the FormShown event. Quick and dirty.

"Peter Duniho" <Np*********@nnowslpianmk.comwrote in message
news:op***************@petes-computer.local...
On Sat, 08 Dec 2007 15:10:21 -0800, Joey Joe Joe <no@nothing.comwrote:
>>>Is there some quick and dirty I can do for the start-up to make the
form appear before the animation starts (or even finishes!)?

Yes. Try the FormShown event instead. The FormLoad event is NOT the
place to try animation.

Neither is FormShown. _Starting_ some animation process might be
appropriate there, and would be just as appropriate in the FormLoad
event. Actually _doing_ the animation belongs there no more than it
belongs in FormLoad.
Dec 9 '07 #7

P: n/a

"Joey Joe Joe" <no@nothing.comwrote in message
news:eF*************@TK2MSFTNGP04.phx.gbl...
>>>Is there some quick and dirty I can do for the start-up to make the form
appear before the animation starts (or even finishes!)?

Yes. Try the FormShown event instead. The FormLoad event is NOT the place
to try animation.
Yes, thankyou. Changing "Form_Load" to "Form_Show" solved the problem. I
suspect that this would also have solved my original problem (the PictureBox
coming up blank, instead of showing the default setup screen), without my
needing to redevelop this using a custom control and OnPaint. Having said
that, I am still pleased that I investigated and implemented OnPaint, at
least I now know how to implement V2 "more properly", and learned a lot in
the process. As it is, V1.0 is now ready to ship - thankyou again.
Dec 9 '07 #8

P: n/a

Thankyou for your advice now, and previously.

My app actually now works extremely well, at least on my computer. By having
it entirely single threaded, I also know that it will work on any computer -
there is obviously far less scope for timing issues etc if my app runs that
way.

Having said that, your advice has always been excellent, and if and when I
decide to develop V2 it will be done in the way you suggest (as will any
future programs).

Actually, there is a little bit of multi-threading/re-entrancy in my code.
When I kick-off the animation, I turn off all the buttons that "do" things
that aren't reasonable to change in the middle of an animation. I leave a
"Stop" button, and some options to do with the animation - level of detail,
speed, etc. I have a DoEvents call after each animation loop that allows the
buttons to respond during the animation. Clicking these buttons just set
flags and values used in the animation loop; these are all read-only in the
animation so critical races cannot occur.

I don't know how fast it runs, but having a Thread.Sleep(10) after each
animation frame makes a noticeable difference to the speed, so its pretty
fast. Considering that I redraw my entire 1000 x 768 bitmap on every loop,
its pretty good - almost a million points are copied on every animation. I
can speed this up by redrawing only the part that has changed, as you
suggested - difficult to do with how the code is implemented now (as the
draws are intermingled with the position logic code) but when I develop V2 I
will clean this up in the manner you suggest.

Again, thankyou for your advice and help.
Peter Webb

Dec 9 '07 #9

P: n/a
On Sat, 08 Dec 2007 16:50:33 -0800, Joey Joe Joe <no@nothing.comwrote:
I never said that the FormShown event was the optimum place to do
animation.
This isn't about what's "optimum". It's about what's correct. The world
already has plenty of crummy software. No one needs our help adding to
the list.
I was answering a specific question from the poster which is, "Is there
some quick and dirty I can do for the start-up to make the form appear
before the animation starts (or even finishes!)?" Did you notice the
quick and dirty reference? Hence, I offered the FormShown event. Quick
and dirty.
It's worse than quick and dirty. It's pure, unadulterated excrement.
Granted, it's just my opinion, but that opinion is that we as a community
have no business helping other people make .NET applications _worse_.
Which is just what you've done.

Note that now that Webb has a hack that appears to him to get done what he
thinks he wants done, he has no interest in making the application work
correctly before he releases it on whatever unsuspecting customers he's
got. So now, whatever that application does (I seem to recall something
about an orbit simulation or something), anyone who wants to do anything
with the application other than watch whatever animation is going on
(like, for example, exit the application...a basic function that every
user has the right to expect from any application) isn't going to be able
to do it.

Some questions are best left unanswered.

Pete
Dec 9 '07 #10

P: n/a
On Sat, 08 Dec 2007 23:01:01 -0800, Peter Webb
<we********@DIESPAMDIEoptusnet.com.auwrote:
[...]
Actually, there is a little bit of multi-threading/re-entrancy in my
code. When I kick-off the animation, I turn off all the buttons that
"do" things that aren't reasonable to change in the middle of an
animation. I leave a "Stop" button, and some options to do with the
animation - level of detail, speed, etc. I have a DoEvents call after
each animation loop that allows the buttons to respond during the
animation.
Ah. Great. You've compounded the original error with another, which is
the use of DoEvents(). :(

Oh well...at least your users can exit the application that way.
[...]
Again, thankyou for your advice and help.
I guess I should be glad you found my posts helpful. From here though, I
don't really see that any of my advice wound up in your program. Mostly,
I'm just sad about the whole thing.

Pete
Dec 9 '07 #11

P: n/a
_Peter_,

Man, you're _smug_ and arrogant. I bet it's not easy going though _life_ so
smug. Yet, you seem to do it with such ease. Granted, that's just _my_
opinion, but it's probably the opinion of others as well.
have no business helping other people make .NET applications _worse_.
Maybe _you're_ the reason for lousy software. People are afraid to ask
questions for fear of being ridiculed by newsgroup bullies. Did you ever
think of that? I guess calling someone's project _excrement_ is one way to
assure that they will continue to _ask_ for help in the future. Nice going,
genius.
Some questions are best left unanswered
Maybe you should _take_ your own advice for a change. I think the _reason_
you answer so many questions is because you need approval. You want people
to _say_, "Peter, you're a genius. Thanks for you help." I bet your chest
_inflates_ every time you read something like that. _No_?

"Peter Duniho" <Np*********@nnowslpianmk.comwrote in message
news:op***************@petes-computer.local...
On Sat, 08 Dec 2007 16:50:33 -0800, Joey Joe Joe <no@nothing.comwrote:
>I never said that the FormShown event was the optimum place to do
animation.

This isn't about what's "optimum". It's about what's correct. The world
already has plenty of crummy software. No one needs our help adding to
the list.
>I was answering a specific question from the poster which is, "Is there
some quick and dirty I can do for the start-up to make the form appear
before the animation starts (or even finishes!)?" Did you notice the
quick and dirty reference? Hence, I offered the FormShown event. Quick
and dirty.

It's worse than quick and dirty. It's pure, unadulterated excrement.
Granted, it's just my opinion, but that opinion is that we as a community
have no business helping other people make .NET applications _worse_.
Which is just what you've done.

Note that now that Webb has a hack that appears to him to get done what he
thinks he wants done, he has no interest in making the application work
correctly before he releases it on whatever unsuspecting customers he's
got. So now, whatever that application does (I seem to recall something
about an orbit simulation or something), anyone who wants to do anything
with the application other than watch whatever animation is going on
(like, for example, exit the application...a basic function that every
user has the right to expect from any application) isn't going to be able
to do it.

Some questions are best left unanswered.

Pete
Dec 9 '07 #12

This discussion thread is closed

Replies have been disabled for this discussion.