clear fillEllipse graphics

S

Slickuser

I have this function that will fill the ellipse every 10 seconds with
specific x,y,w,h.

Now I want do the the reverse, to clear the ellipse with given x,y
using Timer at every 30s.
Or I have put these (x,y,w,h) to an array?
Later go back the array and fill ellipse with the same color as
background?

Is there an easy way?

Thanks..

//Sample code that draw with timer

private void drawNote(int x, int y, int width, int height,
SolidBrush brush)
{
Graphics graphicGlobal = this.CreateGraphics();
this.Show();

graphicGlobal.FillEllipse(brush, x, y, width, height);

}

// Timer to draw
private void timer_note_Tick(object sender, EventArgs e)
{
SolidBrush colorBrush = new SolidBrush(Color.Cyan);
SolidBrush colorBrush2 = new SolidBrush(Color.Magenta);

drawNote(x_global, y_global, width_global, height_global,
colorBrush);

drawNote(x_global + 15, y_global + 15, width_global,
height_global, colorBrush2);

x_global = x_global + 30;
y_global = y_global + 30;
}
 
F

Family Tree Mike

Slickuser said:
I have this function that will fill the ellipse every 10 seconds with
specific x,y,w,h.

Now I want do the the reverse, to clear the ellipse with given x,y
using Timer at every 30s.
Or I have put these (x,y,w,h) to an array?
Later go back the array and fill ellipse with the same color as
background?

Is there an easy way?

Thanks..

//Sample code that draw with timer

private void drawNote(int x, int y, int width, int height,
SolidBrush brush)
{
Graphics graphicGlobal = this.CreateGraphics();
this.Show();

graphicGlobal.FillEllipse(brush, x, y, width, height);

}

// Timer to draw
private void timer_note_Tick(object sender, EventArgs e)
{
SolidBrush colorBrush = new SolidBrush(Color.Cyan);
SolidBrush colorBrush2 = new SolidBrush(Color.Magenta);

drawNote(x_global, y_global, width_global, height_global,
colorBrush);

drawNote(x_global + 15, y_global + 15, width_global,
height_global, colorBrush2);

x_global = x_global + 30;
y_global = y_global + 30;
}

Reread the advise to you in your post yesterday. It looks like you are
going back to drawing graphics outside the paint event.
 
S

Slickuser

If I use that way, I need some how to pass in a PaintEvents to draw.
That how why I choose doing this way.
 
P

Peter Duniho

If I use that way, I need some how to pass in a PaintEvents to draw.

No, you don't. Windows generates the PaintEventArgs. You don't call
OnPaint() yourself, and you don't raise the Paint event yourself. You
use Control.Invalidate() to tell Windows what area of the control needs
to be redrawn, and Windows handles the rest.
That how why I choose doing this way.

You are doing it wrong. You will continue to have problems as long as
you continue to do it wrong.

You can find a discussion on proper techniques for custom drawing in .NET here:
http://msdn2.microsoft.com/en-us/library/kxys6ytf.aspx

While it focuses on an actual custom control, the concepts are the same
whether you are overriding OnPaint() or just handling the Paint event
of a Control instance.

You may also want to learn more about the underlying Windows drawing
model, which is documented here
http://msdn2.microsoft.com/en-us/library/ms534857.aspx

Note that this model is not unique to Windows. It's a common paradigm,
and so if you learn how to do it in Windows, you will be familiar with
drawing in many different GUI platforms. More importantly, because its
close relationship with the basic .NET drawing model, learning about
the unmanaged Windows paradigm will actually help you understand the
..NET model better as well.

Finally, you may want to consider learning about the Windows
Presentation Foundation (WPF). While I don't know much about it
myself, my understanding is that it's a more declarative paradigm, as
opposed to the event-driven paradigm you're trying to use in a sort of
declarative way.
http://msdn2.microsoft.com/en-us/library/ms754130.aspx

Pete
 
S

Slickuser

I am looking through examples right now.

Can you give me example that let call(or not) a function which draw at
x,y,w,h with PaintEventArgs?

I still want to call this function with this argument when I want it
to draw: drawNote(int x, int y, int width, int height, SolidBrush
brush)

private void drawNote(int x, int y, int width, int
height,SolidBrush brush)
{
////PaintEventArg???
//Graphics graphicGlobal = this.CreateGraphics();
/// this.Show();

graphicGlobal.FillEllipse(brush, x, y, width, height);

}
 
P

Peter Duniho

I am looking through examples right now.

Eventually, you should find a discussion of the Paint event and what
PaintEventArgs contains.
Can you give me example that let call(or not) a function which draw at
x,y,w,h with PaintEventArgs?

In that discussion, you will find that the PaintEventArgs.Graphics member
provides the Graphics instance to which you need to draw.
I still want to call this function with this argument when I want it
to draw: drawNote(int x, int y, int width, int height, SolidBrush
brush)

Since the PaintEventArgs.Graphics member is the Graphics instance to which
you need to draw, that means that you need to pass the Graphics instance
from PaintEventArgs to any code that wants to draw. The simplest way to
do that would be to include it as a parameter to the method called from
the OnPaint() method or the Paint event handler (however you've decided to
implement it).

So:
private void drawNote(int x, int y, int width, int
height,SolidBrush brush)
becomes:

private void drawNote(Graphics gfx, int x, int y, int width, int
height,SolidBrush brush)

and the method body:
{
////PaintEventArg???
//Graphics graphicGlobal = this.CreateGraphics();
/// this.Show();

graphicGlobal.FillEllipse(brush, x, y, width, height);

}
becomes:

{
gfx.FillEllipse(brush, x, y, width, height);
}

Hope that helps.

Pete
 
S

Slickuser

Which in that case, now I need to pass in Graphics as input argument
to call drawNote (5 input arguments)?

How can I achieve with my timer? Thank you so much for your help.

// Timer to draw
private void timer_note_Tick(object sender, EventArgs e)
{
SolidBrush colorBrush = new SolidBrush(Color.Cyan);
SolidBrush colorBrush2 = new SolidBrush(Color.Magenta);

//drawNote(x_global, y_global, width_global,
height_global,colorBrush);

//drawNote(x_global + 15, y_global + 15,
width_global,height_global, colorBrush2);

x_global = x_global + 30;
y_global = y_global + 30;
}
 
S

Slickuser

I should use ??

protected override void OnPaint(PaintEventArgs e)
{
// If there is an image and it has a location,
// paint it when the Form is repainted.
base.OnPaint(e);
//base.Invalidate();

}
 
P

Peter Duniho

Which in that case, now I need to pass in Graphics as input argument
to call drawNote (5 input arguments)?

Yes. If it bothers you and your drawing code is complex enough, you may
find it makes sense to create a class instantiated for each time you draw
and to which you pass the Graphics instance as well as any other
frequently-used data items you might want access to from the drawing code.
How can I achieve with my timer? Thank you so much for your help.

You don't draw in response to a timer. You can update your data, and then
invalidate the area of the control/form that needs to be redrawn
(depending on the data and what you're drawing, this might just be the
whole control or form).

Pete
 
P

Peter Duniho

I should use ??

protected override void OnPaint(PaintEventArgs e)
{
// If there is an image and it has a location,
// paint it when the Form is repainted.
base.OnPaint(e);
//base.Invalidate();

}

That depends. Are you putting the drawing code into the same class as
that which represents the control or form into which the drawing is
actually being done? If so, then yes...I personally feel that writing an
override for OnPaint() is more appropriate than handling the Paint event.

Note: you almost always will want to call the base.OnPaint() as you've
shown there. However, you should _never_ call Invalidate() from within
OnPaint() or a Paint event handler. I realize the line is commented out
here, but it should never have been there in the first place.

Pete
 
S

Slickuser

Yes. If it bothers you and your drawing code is complex enough, you may
find it makes sense to create a class instantiated for each time you draw
and to which you pass the Graphics instance as well as any other
frequently-used data items you might want access to from the drawing code.


You don't draw in response to a timer. You can update your data, and then
invalidate the area of the control/form that needs to be redrawn
(depending on the data and what you're drawing, this might just be the
whole control or form).

Pete


Update my data of x,y,...?


Here is my code so far..


//code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace fill3
{
public partial class Form1 : Form
{

int x_global = 10;
int y_global = 10;
int width_global = 50;
int height_global = 50;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{

}

private void timer_note_Tick(object sender, EventArgs e)
{
SolidBrush colorBrush = new SolidBrush(Color.Cyan);
SolidBrush colorBrush2 = new SolidBrush(Color.Magenta);

//drawNote(x_global, y_global, width_global,
height_global, colorBrush);
//drawNote(x_global + 15, y_global + 15, width_global,
height_global, colorBrush2);

x_global = x_global + 30;
y_global = y_global + 30;
}


private void drawNote(Graphics gfx, int x, int y, int width,
int height,SolidBrush brush)
{
gfx.FillEllipse(brush, x, y, width, height);

gfx.Dispose();
this.Invalidate();
}

}
}
 
P

Peter Duniho

[...]
Update my data of x,y,...?
Yes.

Here is my code so far..

Here's what it should look like instead:
[...]
private void timer_note_Tick(object sender, EventArgs e)
{
x_global = x_global + 30;
y_global = y_global + 30;
Invalidate();
}


private void drawNote(Graphics gfx, int x, int y, int width,
int height,SolidBrush brush)
{
gfx.FillEllipse(brush, x, y, width, height);
}

protected override void OnPaint(object sender, PaintEventArgs
pea)
{
using(SolidBrush colorBrush = new SolidBrush(Color.Cyan),
colorBrush2 = new SolidBrush(Color.Magenta))
{
drawNote(pea.Graphics, x_global, y_global,
width_global, height_global, colorBrush);
drawNote(pea.Graphics, x_global + 15, y_global + 15,
width_global, height_global, colorBrush2);
}
}

Note that I removed the Dispose() and Invalidate() calls from your
drawNote() method. Those are _both_ extremely important to NOT do while
handling a Paint event. The Dispose() is bad because you didn't create
the Graphics instance and it needs to stick around until Windows is ready
to dispose it itself. The Invalidate() is bad because every time you call
Invalidate(), that will cause another Paint event to be raised, creating
an endless loop.

Also note the "using" statement in the OnPaint() method. You _do_ create
the SolidBrush objects, and they must be disposed when you're done with
them. The using() statement provides a convenient way to do that.

Finally note that the above doesn't accumulate the ellipses. It's not
clear from your question or code whether you expect it to. But if you
want to gradually add new ellipses without erasing the previously drawn
ones, you'll need to manage that in addition to the code shown above.
IMHO, we should straighten out the basic drawing mechanism first though.
Baby steps. :)

Pete
 
P

Peter Webb

Peter:

I have been following this thread, as I have similar problems/issues (as you
may recall).

Since your last round of advice, I have played a lot, and even bought a C#
book, but its still not working. After many hours without luck, I would
really appreciate some more help

In my case, the OnPaint command would be extremely easy to implement as I am
drawing entirely to a graphics buffer (so I have a complete copy of the
image) and all I need to do is execute a MyBuffer.Render() to physically
redraw.

Here is my problem.

I have put this code into my app:

protected override void OnPaint(object sender, PaintEventArgs
pea)
{
MyBuffer.Render();
}

This actually works, but only sort-of. This OnPaint is triggered by a redraw
of Form1. I start my app, it comes up with a blank PictureBox1. As soon as I
move the mouse off the PictureBox (eg to the button area) it triggers the
above and my image appears. But only (very annoyingly) when the user moves
the mouse to the button area. This is very inconvenient because I can't play
my animation on start up (and it looks tacky).

Clearly, I have wired the OnPaint to the Form, and not to PictureBox1.

My book says that I need to insert the OnPaint override in the control
setup. I can do this for Form1, because the system generates a block of code
to initialise Form1. I can find no equivalent for PictureBox1.

If I click on my PictureBox1 in the designer, it has a Paint method
(member?) listed, but this is Paint but not OnPaint. I can't see where my
On_Paint method has to be inserted in my code to trigger off PictureBox1
instead of Form1.

Somebody suggested that I create a custom control which inherits from
PictureBox but overrides OnPaint. I tried this, but got into a fair amount
of mess. I have abandoned this as it seemed a very complicated solution to a
pretty simple problem, and I got even further out of my depth.

So, is there a simple way of wiring my OnPaint method (which works fine) to
be triggered off a repaint of PictureBox1 instead of Form1? If so, how do I
do it?

Thanks,



Peter Webb
 
S

Slickuser

[...]
Update my data of x,y,...?
Yes.

Here is my code so far..

Here's what it should look like instead:
[...]
private void timer_note_Tick(object sender, EventArgs e)
{
x_global = x_global + 30;
y_global = y_global + 30;
Invalidate();
}
private void drawNote(Graphics gfx, int x, int y, int width,
int height,SolidBrush brush)
{
gfx.FillEllipse(brush, x, y, width, height);
}

protected override void OnPaint(object sender, PaintEventArgs
pea)
{
using(SolidBrush colorBrush = new SolidBrush(Color.Cyan),
colorBrush2 = new SolidBrush(Color.Magenta))
{
drawNote(pea.Graphics, x_global, y_global,
width_global, height_global, colorBrush);
drawNote(pea.Graphics, x_global + 15, y_global + 15,
width_global, height_global, colorBrush2);
}
}

Note that I removed the Dispose() and Invalidate() calls from your
drawNote() method. Those are _both_ extremely important to NOT do while
handling a Paint event. The Dispose() is bad because you didn't create
the Graphics instance and it needs to stick around until Windows is ready
to dispose it itself. The Invalidate() is bad because every time you call
Invalidate(), that will cause another Paint event to be raised, creating
an endless loop.

Also note the "using" statement in the OnPaint() method. You _do_ create
the SolidBrush objects, and they must be disposed when you're done with
them. The using() statement provides a convenient way to do that.

Finally note that the above doesn't accumulate the ellipses. It's not
clear from your question or code whether you expect it to. But if you
want to gradually add new ellipses without erasing the previously drawn
ones, you'll need to manage that in addition to the code shown above.
IMHO, we should straighten out the basic drawing mechanism first though.
Baby steps. :)

Pete

Thank you so much Pete! You're awesome!

I'll add these basic drawing to my code. I'll reply back if run into
any more problems.
 
P

Peter Duniho

[...]
So, is there a simple way of wiring my OnPaint method (which works fine)
to be triggered off a repaint of PictureBox1 instead of Form1? If so,
how do I do it?

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.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top