.NET Forum / Languages / C# / December 2007
clear fillEllipse graphics
|
|
Thread rating:  |
Slickuser - 30 Nov 2007 06:35 GMT 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; }
Hilton - 30 Nov 2007 07:02 GMT I think you need to dispose of graphicGlobal.
>I have this function that will fill the ellipse every 10 seconds with > specific x,y,w,h. [quoted text clipped - 36 lines] > y_global = y_global + 30; > } Family Tree Mike - 30 Nov 2007 12:53 GMT > I have this function that will fill the ellipse every 10 seconds with > specific x,y,w,h. [quoted text clipped - 36 lines] > 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.
Slickuser - 30 Nov 2007 20:03 GMT If I use that way, I need some how to pass in a PaintEvents to draw. That how why I choose doing this way.
On Nov 30, 4:53 am, Family Tree Mike <FamilyTreeM...@discussions.microsoft.com> wrote:
> > I have this function that will fill the ellipse every 10 seconds with > > specific x,y,w,h. [quoted text clipped - 41 lines] > > - Show quoted text - Peter Duniho - 30 Nov 2007 21:15 GMT > 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
Slickuser - 01 Dec 2007 01:14 GMT 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);
}
> > If I use that way, I need some how to pass in a PaintEvents to draw. > [quoted text clipped - 31 lines] > > Pete Peter Duniho - 01 Dec 2007 01:23 GMT > 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??? [quoted text clipped - 4 lines] > > } becomes:
> { > gfx.FillEllipse(brush, x, y, width, height); > } Hope that helps.
Pete
Slickuser - 01 Dec 2007 01:48 GMT 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; }
On Nov 30, 5:23 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > I am looking through examples right now. > [quoted text clipped - 48 lines] > > Pete Slickuser - 01 Dec 2007 01:51 GMT 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();
}
> Which in that case, now I need to pass in Graphics as input argument > to call drawNote (5 input arguments)? [quoted text clipped - 72 lines] > > > Pete Peter Duniho - 01 Dec 2007 02:10 GMT > I should use ?? > [quoted text clipped - 6 lines] > > } 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
Peter Duniho - 01 Dec 2007 02:07 GMT > 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
Slickuser - 01 Dec 2007 02:22 GMT On Nov 30, 6:07 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Which in that case, now I need to pass in Graphics as input argument > > to call drawNote (5 input arguments)? [quoted text clipped - 12 lines] > > 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(); }
} }
Peter Duniho - 01 Dec 2007 04:11 GMT [...]
>> You don't draw in response to a timer. You can update your data, and >> then [quoted text clipped - 3 lines] > > 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) [quoted text clipped - 9 lines] > 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
Peter Webb - 02 Dec 2007 01:59 GMT 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
Peter Duniho - 02 Dec 2007 02:41 GMT > [...] > 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
Slickuser - 02 Dec 2007 02:07 GMT On Nov 30, 8:11 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> [...] > [quoted text clipped - 59 lines] > > 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.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|