Form1_Paint event - c#

i wonder how it works..
i have this code. it should paint pictures (Bitmap images ) on the form..constantly. but i dont know how often it is triggered. i need it to be triggered very often (at least every 1-2 seconds). i need it to send the parameters to another object that i have (Game game).. so game object will draw everything
public void Form1_Paint(object sender, EventArgs e)
{
//the animation has 4 cell to draw, so the arguments are passed to the game objects instructing it to which cells to draw.
using (Graphics g = this.CreateGraphics())
{
game.Draw(g, animationTimerCounter);
}
}
when is the event being fired?

A paint event is called when the form (or part of it) has to be redrawn, e.g. the form is moved, or another window has hidden a part of it etc.
You can force a Paint event by calling yourControl.Invalidate() method.
In your case you could use for example a Timer to force a Paint with the desired frequency (e.g. every 1-2 seconds).

Paint is basically triggered as required, events that trigger paint can be examples such as (there are others):
A form that was in front is moved
You form resizes
your form is restored
Is this XNA? it kinda looks like it is a similar effect. XNA is a good framework for what it looks like you're trying to do.
If you need your form to paint, you can invalidate it or tell it to paint.

The Paint event is raised when the control is redrawn. Control.Paint Event in MSDN

Related

How to redraw quickly on a canvas. C# WINFORM

For my software, I am using a Timer from Systems.timer library, every time my timer ticks, it calls a method for repainting my screen. I do not want to clear the screen, then to repaint on it. I just want to paint the new areas on it directly.
At the beginning, I did this:
Constructor{
...
this.timer = new Timer
{
Interval = 10,
};
this.timer.Elapsed += OnPaint;
this.timer.start();
}
public void OnPaint(Object sender, EventArgs e)
{
This.Parent.OnPaintLoadingCircle();
This.Parent.OnPaintReadyToBePaintedAreas();
}
Then I noticed it was much faster for painting when the OnPaint method contains this:
public void OnPaint(Object sender, EventArgs e)
{
This.Parent.Invalidate();
}
So I have two questions:
QUESTION 1 :
Why is it faster???
Because when I call invalidate():
The UI thread clears the screen.
Then UI thread redraws the old areas
Then UI thread draws the loading circle
Then UI thread draws the new areas.
And when I call my two methods OnPaintLoadingCircle() and OnPaintReadyToBePaintedArea():
The timer thread draws the loading circle
Then the timer thread draws the new areas
QUESTION 2 :
I would like to know if it exists a way for asking a controller to draw it surface without clearing it. ( I tried this.Parent.Update(), this.Parent.Refresh(), both of them first clear the screen as well).
Thank you very much for helping me.
Why is it faster???
For the simplest of reasons: because when you call Invalidate() in the OnPaint() method, it forces re-painting of the window immediately, which is much more quickly than a timer could.
The timers in .NET are not suited for high-frequency operations. They only guarantee the time between intervals will be at least what you specify. The actual interval can and often is longer than what you specify, especially if you are using a very short interval (e.g. on the order of less than 10-20ms). This necessarily limits how often you can re-paint the window when using a timer, to a much greater degree than just re-painting the window as fast as you can.
I would like to know if it exists a way for asking a controller to draw it surface without clearing it.
Not easily, no. At the most basic level, you can override OnPaintBackground() and not call the base implementation. But this approach only works if you are prepared to redraw everything, because the system counts on you covering up stale pixels with the correct pixels when you draw.
In fact, a much more common approach is to use double-buffering. The most basic form is to just set the DoubleBuffered property in the control constructor. But you can also combine not clearing the window with maintaining your own offscreen Bitmap object into which you draw your content. Then when a Paint event happens, you just copy the Bitmap to the window.
A much more complicated approach involves hosting a Direct2D surface in your window. Not for the faint of heart, but should offer the best possible performance in a Winforms program.

Drawing glitches when using CreateGraphics rather than Paint event handler for custom drawing

I've written a Windows Forms app where I do custom drawing on a Panel using Control.CreateGraphics(). Here's what my Form looks like at startup:
The custom drawing is performed on the top panel in the Click event handler of the "Draw!" button. Here's my button click handler:
private void drawButton_Click(object sender, EventArgs e)
{
using (Graphics g = drawPanel.CreateGraphics())
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.Clear(Color.White);
Size size = drawPanel.ClientSize;
Rectangle bounds = drawPanel.ClientRectangle;
bounds.Inflate(-10, -10);
g.FillEllipse(Brushes.LightGreen, bounds);
g.DrawEllipse(Pens.Black, bounds);
}
}
After a click on drawButton, the form looks like this:
Success!
But when I shrink the form by dragging a corner...
...and expand it back to its original size,
part of what I drew is gone!
This also happens when I drag part of the window offscreen...
...and drag it back onscreen:
If I minimize the window and restore it, the whole image is erased:
What is causing this? How can I make it so the graphics I draw are persistent?
Note: I've created this self-answered question so I have a canonical Q/A to direct users to, as this is a common scenario that's hard to search for if you don't already know the cause of the problem.
TL;DR:
Don't do your drawing in response to a one-time UI event with Control.CreateGraphics. Instead, register a Paint event handler for the control on which you want to paint, and do your drawing with the Graphics object passed via the PaintEventArgs.
If you want to paint only after a button click (for example), in your Click handler, set a boolean flag indicating that the button has been clicked and then call Control.Invalidate(). Then do your rendering conditionally in the Paint handler.
Finally, if your control's contents should change with the size of the control, register a Resize event handler and call Invalidate() there too.
Example code:
private bool _doCustomDrawing = false;
private void drawPanel_Paint(object sender, PaintEventArgs e)
{
if (_doCustomDrawing)
{
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.Clear(Color.White);
Size size = drawPanel.ClientSize;
Rectangle bounds = drawPanel.ClientRectangle;
bounds.Inflate(-10, -10);
g.FillEllipse(Brushes.LightGreen, bounds);
g.DrawEllipse(Pens.Black, bounds);
}
}
private void drawButton_Click(object sender, EventArgs e)
{
_doCustomDrawing = true;
drawPanel.Invalidate();
}
private void drawPanel_Resize(object sender, EventArgs e)
{
drawPanel.Invalidate();
}
But why? What was I doing wrong, and how does this fix it?
Take a look at the documentation for Control.CreateGraphics:
The Graphics object that you retrieve through the CreateGraphics method should not normally be retained after the current Windows message has been processed, because anything painted with that object will be erased with the next WM_PAINT message.
Windows doesn't take responsibility for retaining the graphics you draw to your Control. Rather, it identifies situations in which your control will require a repaint and informs it with a WM_PAINT message. Then it's up to your control to repaint itself. This happens in the OnPaint method, which you can override if you subclass Control or one of its subclasses. If you're not subclassing, you can still do custom drawing by handling the public Paint event, which a control will fire near the end of its OnPaint method. This is where you want to hook in, to make sure your graphics get redrawn every time the Control is told to repaint. Otherwise, part or all of your control will be painted over to the control's default appearance.
Repainting happens when all or part of a control is invalidated. You can invalidate the entire control, requesting a full repaint, by calling Control.Invalidate(). Other situations may require only a partial repaint. If Windows determines that only part of a Control needs to be repainted, the PaintEventArgs you receive will have a non-empty ClipRegion. In this situation, your drawing will only affect the area in the ClipRegion, even if you try to draw to areas outside that region. This is why the call to drawPanel.Invalidate() was required in the above example. Because the appearance of drawPanel needs to change with the size of the control and only the new parts of the control are invalidated when the window is expanded, it's necessary to request a full repaint with each resize.

How to paint additional things on a drawn panel?

I'm reading a lot about C# drawing and reading MSDN tutorial on GDI+ using Graphics handlers.
I want to be able to paint a graph, which nodes I have in a list but I can't use auto-placing, I need the nodes to be in a specified place and have specific appearance and so on, changing over time, that's why I stopped looking for graph libraries.
It all works great when painted for the first time but when I want something painted after something else happens in the code (not after clicking the control), I can't do it. For example:
if (somethingHappens) {
// repaint the panel adding some things
}
All I got is either nothing new happens or my earlier painting disappears.
I found some examples on OnPaint overriding and drawings disappearing when minimalized. When I need to paint something additional when something happens in an application, do I have to override it or is it completely different?
I looked for another Q&A that would include the information you need. Frankly, it's a fundamental question, how to properly deal with drawing a Forms control. MSDN topics like Overriding the OnPaint Method and Custom Control Painting and Rendering provide some detail on the right way to do this. But I'm surprised I wasn't able to find any StackOverflow Q&A that at least addresses this basic question directly (there are many which address it tangentially of course).
Anyway…
This is the basic sequence for drawing in Forms code:
Generate some data to be drawn
Invalidate the control where the data will be drawn
Handle the Paint event by actually drawing that data
Repeat the above as necessary, i.e. any time the data changes (such as "something happens", as in your question) you move back to step #1, adding whatever new data you want to your existing collection of data, then invalidating the control, and finally responding to the Paint event in your event handler.
In the case of drawing a graph, this might look something like this:
private List<Point> _points = new List<Point>();
void AddPoint(Point point)
{
_points.Add(point);
panel1.Invalidate();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
if (_points.Count < 2)
{
return;
}
Point previousPoint = _points[0];
for (int i = 1; i < _points.Count; i++)
{
currentPoint = _points[i];
e.Graphics.DrawLine(Pens.Black, previousPoint, currentPoint);
previousPoint = currentPoint;
}
}
Note that the panel1_Paint() event is an event handler. Normally you would create this via the Designer by selecting the Panel object, switching to the "Events" list in the "Properties" window for the control, and double-clicking in the edit field for the Paint event.
That is of course the simplest example. You can add things like updating the data in a batched manner (i.e. don't invalidate the control until you've added a group of points), use different colors or line styles to draw, draw other elements of the graph like axes, ticks, legend, etc. The above is simply meant to illustrate the basic technique.
One final note: depending on how many points in your graph you need to draw, the above may or may not be fast enough. It should be fine up to thousands of points or so, but if you start getting to tens or hundreds of thousands or more, you'll probably find that it's useful to cache the drawing into a bitmap and draw just the bitmap. But that's a whole separate question. First, you need to make sure you understand the Forms drawing model and are using it correctly.

Translate and rotate image outside of glControl1_Paint using OpenTK?

I am making CAD type software in VS2010 Pro using a C# Windows Form Application and OpenTK. Nothing fancy; I just want to be able to read in some basic shapes and draw them. I'm not sure if this makes a difference to the answer, but I am drawing in 2D space using GL.Ortho();
To get familiar with graphics I've done a few OpenTK examples straight from the OpenTK documentation and have a basic understanding of it. From what I've learned so far I cannot move/rotate my primitives unless they were created within this event:
private void glControl1_Paint(object sender, PaintEventArgs e)
{
}
My program launches and waits for the user to select the CAD file to read in. After I read the file and break it down into primitives I draw it to the glControl1 form. So far it works as expected. However, I do not draw it in the "glControl1_Paint" event. Thus I have no control to translate/rotate it by using keyboard/mouse inputs.
I have read answers to other questions where the asker was directed to draw in the "glControl1_Paint" event. I would love to because it would solve my problem, but I am not sure how to do that since I don't have the primitives upon launch of the application, I wait for the user to provide the data.
I suppose I have a few questions that I would like to know the answers to:
1) When does the "glControl1_Paint" event happen in the program? I assumed it was part of initializing the glControl1 window and fired upon startup. Can I control when this happens so that I can draw my primitives here? If so, how do I control when this happens and how do I pass my geometry into this?
2) Is there a way to translate/rotate the my primitives outside of the "glControl1_Paint" event?
No you can not know when paint event will trigger. But you can manually trigger it via Invalidate() function.
The flow should be like this.
You should do the all the drawing in your paint event.
If something happened that effects the drawing, you should call Invalidate()
Keyboard events that moves objects or mouse events that rotates camera etc. all of them should call Invalidate()
If you like maximum frame rate. you should override application main loop and make it call Invalidate() if there are no other windows messages to process.
here is my programming loop
static void Main()
{
...
MainForm mainFrom = new MainForm();
mainFrom.FormClosed += QuitLoop;
mainFrom.Show();
do
{
Application.DoEvents();
mainFrom.glControl1.Invalidate(true); //actually may program is a lot more complex than this
if (mainFrom.IsRunning)
System.Threading.Thread.Sleep(0);
else
System.Threading.Thread.Sleep(1);
} while (!mQuit);
...

.NET UserControl: Size property gives incorrect value on Resize event

Excuse the code dump, these are functions within a UserControl
private void PNGQuantPreviewControl_Resize(object sender, EventArgs e)
{
createOffScreenBm();
draw();
}
private void createOffScreenBm()
{
offScreenBm = new Bitmap(this.Size.Width, this.Size.Height);
offScreenGfx = Graphics.FromImage(offScreenBm);
}
private void draw()
{
// draw background
offScreenGfx.FillRectangle(transTexture, 0, 0, offScreenBm.Width, offScreenBm.Height);
// draw image preview
offScreenGfx.DrawImage(pngQuantPreview, getTopLeftPosition());
// apply to picture box
this.CreateGraphics().DrawImage(offScreenBm, 0, 0);
}
So, when the control changes size, it recreates the offscreen bitmap to reflect the new size and redraws the image.
However, if I quickly resize the control the bitmap doesn't fill it, there's a gap left at the right and/or bottom.
I'm fairly new to C#, so there's probably something obvious I'm doing wrong, or I'm reading the size values at the wrong time. Any ideas?
First of all you need to overwrite OnPaint method, or subscribe to Paint event and draw everything there.
Second you do not need to create offscreen bitmap for double buffering, because in .net already exist class for such purposes BufferedGraphics.
And third, it is much better to create UserControl descedant and enable internal .net double buffering, something like this:
public UserControl2
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
Using this approach you will get double-buffering, and all you need is to draw your graphics in OnPaint method. You can read more about this control styles in Msdn.
Have you considered overriding the OnPaint method and placing the code within that method? This would result in your drawing code being executed any time the control needs to be redrawn, regardless of the reason.
A resize event does not necessarily wait until you are finished resizing the parent container. When the resize event is raised it needs to wait until the code exits before it can capture a new resize event so when the window/control is resized quickly, it can't keep up all that well and what you get is the last time it was able to capture the event, not necessarily the final state of the control ... if that makes any sense.
Do you have anything like a splitter on your control, or a MinSize or MaxSize declared?

Categories