Peter Duniho said:
[...]
I really have appreciated your help, and I would appreciate if you
responded in future.
However, I don't know why you feel it necessary to engage in a pissing
contest with somebody who has in the last few weeks written their first
program in almost 30 years.
I don't see this as a pissing contest. How you get that impression, I'm
not sure. To me, a "pissing contest" implies that both people insist that
they are each superior to the other. I'm not asserting superiority,
except in the sense that I do have more experience in this type of
programming than you do. Even if you were asserting your own superiority
(and you don't seem to be), it takes two to tango.
Yes, you are asserting your own superiority in this domain. Despite the fact
that I acknowledge this, I still get told what a crap job I have done, and
you still go out of your way to make me feel bad about what I have done.
What I do see is repeatedly having provided advice that was repeatedly
ignored. Even after you ran into problems with the wrong design, admitted
it was because of the wrong design, and were provided additional details
on how to do it right, you insisted on doing it wrong.
Here is the overall design advice you gave me a couple of weeks ago:
****************
It seems to me that you actually have two different issues. One is, where
to do the drawing? That is, where does your drawing code go? The other
is, how do you cause the drawing to occur?
With respect to the first question...
The PictureBox class is not for people who want to do their own drawing.
It's a convenient control to which you can attach an existing Image, and
let the control itself deal with a variety of display issues. Such as,
when to draw, if and how to scale the image, that sort of thing.
You _could_ certainly derive a new class from PictureBox and override the
OnPaint() method, but that would be sort of pointless. You can easily do
the same thing deriving from the Control class, without having all the
extra PictureBox functionality dragged along.
You could also handle the PictureBox's Paint event, but again it would be
pointless, for the same reasons.
The reason for deriving from PictureBox would be if you want to use and
extend or override functionality that is specific to the PictureBox
class. So far I haven't seen anything that would suggest that's what
you're doing or what you want.
So where should you do the drawing? IMHO, it depends on what your UI is
like, which I don't know. If you intend to manage all of the drawing
inside the form, then drawing in the form class would be a reasonable
place. If, however, you want to be able to use the VS Designer to
implement and maintain the user interface, and you want to be able to
manipulate where you custom drawing happens as part of that maintenance,
then you will probably want to create a custom Control-derived class.
That way, you'll get a custom control added to your Designer toolbox that
you can drag and drop, placing it wherever you like.
Creating a custom control is very simple, practically the same as creating
a new form class. Just add a new item to the project (using the
Project/Add New Item... menu or the Solution Explorer), and select "Custom
Control" from the template list. This will create a new class derived
from Control. In that class, you can override OnPaint() where you want to
draw.
Of course, you will want to move any other code related to managing the
graphics into that control class, or somewhere related to it. That's a
broader design question, and frankly it's much harder to answer those
questions in a newsgroup like this. Providing good advice on design
generally requires a lot of background knowledge of the problem being
solved, and not only is that background knowledge difficult to express in
this context, so too is explaining how a good design would work.
Suffice to say, a good design makes it easy to put all your pieces
together. Conversely, if you're having trouble putting the pieces
together, you should rethink the design.
Now, finally...there's the question of how to cause the drawing to occur.
The basic mechanism is invalidation of the control. In this context
"invalidation" means something very specific: to communicate to Windows
that an area of the control no longer has valid graphics and needs to be
redrawn. You use the Control.Invalidate() method to do this.
I've already explained that in this thread, and I believe that the various
links that have been posted, by myself and others, also explain it
reasonably well. The basic idea is this though: you don't draw in direct
reaction to changes in the data; you design your drawing code so that it
can always draw whatever the current state of the data is, and you
invalidate the control in reaction to changes to data.
Since you're using the BufferedGraphics class, it seems to me that this
should be especially simple. When you change the BufferedGraphics by
drawing to it, you need to call Invalidate() on whatever control it is
that you are drawing to (the Form class inherits Control as well). If you
know specifically what area of the BufferedGraphics has changed, you can
use that information to restrict the invalidated area, passing a rectangle
to the Invalidate() method so that only the part that changed winds up
getting redrawn.
*******************
I rewrote my app to do it *exactly* this way. I created a custom control
("drawBox"), put my drawing code into it, and triggered screen redraws off
the drawBox1.Invalidate() which calls OnPaint. I did exactly what you
suggested. I didn't even know you could create custom controls, and learned
a lot. It didn't actually solve the problem I had, but I know it was good
advice and so I did it. I thanked you then, and I thank you again.
On top of all that, in the time you spent hacking around the wrong design,
you could have done it right and at the same time actually learned
something that would be useful in building good applications in the
future. I'm not exaggerating. It really doesn't take that much time to
do it right. It takes more time to do it wrong. Especially when you're
still learning how to do it either way.
And that is why I am pleased that I went down your suggested route of using
OnPaint. I didn't solve my problem, but I do know how to do it next time.
So not only did you ignore the advice offered, you didn't save any time
doing it.
I spent several days implementing the suggestion you made. It took a while,
because 6502 Assembler doesn't actually contain an opcode for inhereting the
graphics functionality of base control classes, so there was a bit of a
learning curve.
I can't speak for your 13-year-old daughter. However, part of my career
has been spent writing children's software, and the audience we had would
not appreciate software with bugs in it. They want the program to do what
it's supposed to, including behave like a normal Windows application as
appropriate.
I can't predict what might go wrong here. However, the fact is that
you've created a situation where in response to a particular event
(FormShown, apparently) you create a situation where you never return from
the event. This is antithetical to the correct operation of a Windows
application. The moment you run into a situation where the framework
expected to have completed the FormShown event and fails to do something
correctly because it never has, you're going to run into yet another
problem.
I recognize that you may be able to control the use of the application so
tightly that you never see a problem manifest itself. But I personally do
not believe that just because a program is going to be used casually, or
by a child, that that justifies not putting the same care and quality into
it that one would put in any other program. Your mileage may vary;
obviously you don't agree with that philosophy.
No, I don't agree with that philosophy. This "orbital simulator" is not
going to be used to generate real orbits for NASA spacecraft, and if it was,
I would build it to higher software engineering standards. The suggestion
that I put as much "care and quality" into a toy for my 13 year old daughter
as I would for a piece of code used to calculate space shuttle launch
parameters strikes me as bizarre.
Mostly, however, I'm frustrated at having spent so much time trying to
answer questions about how to do it _right_, and then having all of that
information just ignored. I could have accomplished the same thing by not
replying at all.
You continually make two allegations that are incorrect:
1. I ignored your advice. No, I didn't. I have spent a good part of the last
two weeks completely implementing the suggestion you made above, as I have
done with previous suggestions you have made. As I said, it didn't actually
solve my problem, but it taught me a lot.
2. My app is shit because it is missing basic functionality such as the
ability to stop and pause animations, exit the program, etc. Again, this is
not correct.
I don't consider my time wasted if it helps people. And I don't care what
they are doing. Whether they are working on personal projects, a
commercial retail product, or just some throw-away code so that they can
learn something, I'm happy to answer questions.
Thankyou.
But am I happy to put time and effort into explaining concepts and
details, only to have that information ignored? No. It makes me
decidedly _unhappy_.
It wasn't.
I do need help. I don't need someone telling me that what I have done is
shit. I know that already. Its my first program in 30 years. When I started,
I didn't know that you could call "Button1" whatever you liked. I didn't
know how to attach handlers to events. I didn't know how to extract
information concerning graphic surfaces from event handler arguments. There
are large chunks where I couldn't see how to do it properly in an OO sense
(never having done any OO programming) and which would look at home in a
Fortran program. There are a mass of ad-hoc switches to hack together the
different UI modes I support. My physics model is kludged to prevent
step-size instabilities from accumulating - it seems to work properly for 2
and 3 body problems, but for all I know is completely and totally wrong for
four body problems (or even 3 body problems with boundary conditions that
impose chaotic orbits).
It doesn't really worry me that you keep telling me that what I have done is
not of professional quality. I know its not. It does annoy me that you claim
that I have ignored your advice, when I have spent considerable time doing
*exactly* what you suggested. It also annoys me that you are criticising my
program for not implementing facilities which it does in fact provide. I
don't know why you have to go out of your way to "invent" further
limitations of my program just to prove what a bad program it is, and what a
bad programmer I am. As I said before, I know its shit, and I know I am not
a good C# programmer.
Does your job involve "mentoring" less experienced staff than yourself ? I
bet they just love your motivational tactics, and your ability to publicly
criticise the functionality of applications you haven't even seen. Of
course, you don't need to see the program, the fact that I am writing my
first program in 30 years is immaterial, and just because its a Christmas
present for my 13 year old daughter doesn't excuse me from not building it
to the highest possible engineering standards. You don't even need to know
anything about what my program actually does, and what it is for, to tell me
it is shit.
Anyway, thanks for your advice to implement a drawBox class and use
Invalidate. It took a while, didn't solve my problem, but at least I (sort
of) know how I am supposed to handle redraws. My next go will have
asynchronous update and draw threads; by that time I will hopefully know
enough to make this about C# and .Net to be able to have a go (at least) of
doing it better. In the short term, I intend working out what the hell a
"delegate" is and how to use them; you have mentioned them a couple of times
and so I figure this will be a pre-requisite to me working out how to manage
multiple asynchronous threads.
Peter Webb