In C# WinForms - I am drawing a line chart in real-time that is based on data received via serial port every 500 ms.
The e.Graphics.DrawLine logic is within the form's OnPaint handler.
Once I receive the data from the serial port, I need to call something that causes the form to redraw so that the OnPaint handler is invoked. I have tried this.Refresh and this.Invalidate, and what happens is that I lose whatever had been drawn previously on the form.
Is there another way to achieve this without losing what has been drawn on your form?
The point is that you should think about storing your drawing data somewhere. As already said, a buffer bitmap is a solution. However, if you have not too much to draw, sometimes it is easier and better to store your drawing data in a variable or an array and redraw everything in the OnPaint event.
Suppose you receive some point data that should be added to the chart. Firs of all you create a point List:
List<Point> points = new List<Point>();
Then each time you get a new point you add it to the list and refresh the form:
points.Add(newPoint);
this.Refresh();
In the OnPaint event put the following code:
private void Form_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLines(Pens.Red, points);
}
This works quite fast up to somehow 100 000 points and uses much less memory than the buffer bitmap solution. But you should decide which way to use according to the drawing complexity.
As rerun said, you need to buffer your form (since it appears that you are discarding the data after you draw it).
This is basically how I would do it:
private Bitmap buffer;
// When drawing the data:
if (this.buffer == null)
{
this.buffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
}
// then draw on buffer
// then refresh the form
this.Refresh();
protected override void OnPaint(PaintEventArgs e)
{
if (this.buffer != null)
{
e.Graphics.DrawImage(this.buffer);
}
}
That said, you probably want to cache your data so you can change the size of the buffer when the form size changes and then redraw the old data on it.
The solution may be this.Invalidate()
the default way to handle this is to create a memory bitmap and draw on that then set the image property of the picture box to the memory bitmap.
You will need to store historical data somewhere and just repaint it.
That will be much easier than say caching and clipping bitmaps.
Related
I'm trying to make a floor for a top down style game, where I used to use pictureboxes
I was told that instead of using a Picturebox, I should be using the Graphics.DrawImage(); method, which seems to slightly help but still is very laggy.
My paint function looks like this to draw the background looks like this:
How should I make the drawing less laggy? The image is 2256 by 1504
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(PeanutToTheDungeon.Properties.Resources.tutorialFloor, 0, 0);
}
There are two points that I would make here.
Firstly, DO NOT use Properties.Resources like that. Every time you use a property of that object, the data for that resource gets extracted from the assembly. That means that you are creating a new Image object every time the Paint event is raised, which is often. You should use that resource property once and once only, assign the value to a field and then use that field repeatedly. That means that you will save the time of extracting that data and creating the Image object every time.
The second point may or may not be applicable but, if you are forcing the Paint event to be raised by calling Invalidate or Refresh then you need to make sure that you are invalidating the minimum area possible. If you call Refresh or Invalidate with no argument then you are invalidating the whole for, which means that the whole form will be repainted. The repainting of pixels on screen is actually the slow part of the process, so that's the part that you need to keep to a minimum. That means that, when something changes on your game board, you need to calculate the smallest area possible that will contain all the possible changes and specify that when calling Invalidate. That will reduce the amount of repainting and speed up the process.
Note that you don't need to change your drawing code. You always DRAW everything, then the system will PAINT the part of the form that you previously invalidated.
I was just wondering if anyone could shed some light onto this for me. I've been coding c# for years but never even touched anything in the System.Drawing namespace except for the bitmap class and I've been following some tutorials and came up with some code that works. I'm developing a 2D Game Engine and the code below is for the graphics engine, which uses GDI. However, I just don't understand how this code is even working. Here it is:
private Graphics frontBuffer;
private Graphics backBuffer;
private Bitmap backBufferBitmap;
public void Initialize()
{
backBufferBitmap = new Bitmap(game.Form.Width, game.Form.Height);
frontBuffer = game.Form.CreateGraphics();
backBuffer = Graphics.FromImage(backBufferBitmap);
}
public void Update()
{
try
{
frontBuffer.DrawImageUnscaled(backBufferBitmap,0,0);
backBuffer.Clear(Color.Black);
}
catch(Exception e)
{
throw e;
}
}
So, the main part that's confusing to me is this;
How is the back buffer bitmap getting updated? and why is the back buffer being cleared and not the front buffer?
Also, the initialize method is called once and the update method is called once per frame in a while loop.
After you initialize the backBuffer graphics objects from the bitmap, every time you say, for example backBuffer.DrawLine(...) GDI+ will do the pixel manipulations directly on this Bitmap. They are linked in a way. Think of backBufferBitmap as the canvas, and of backBuffer as the brush.
The frontBuffer is initialized from the form instead. So the form is it's canvas and whatever you do with frontBuffer is drawn to the form - in this case here it draws the backBufferBitmap.
It's basically a double-buffering scheme, that has a lot of advantages over directly drawing your lines and circles to the form, e.g. less flickering. Whenever you draw something to a form, remember that it is removed very often (e.g. when you move the form outside of the screen area). You would need to refresh it using the form's Paint event.
After Update() is called, you would need to redraw your scene to backBuffer, before you call Update again, because the bitmap is blacked out by your Clear() after it is drawn to the screen.
frontBuffer is getting updated because each time you are calling frontBuffer.DrawImageUnscaled(backBufferBitmap,0,0); in update()
backBuffer is gettting cleared because you are calling backBuffer.Clear(Color.Black);
Also, initialize() is supposed to be called only once. At the time of object creation. And I believe it is part of a larger program where parent is calling update() of child.
In my program I have a picture box, containing a bitmap.(300x300 35kB .PNG file)
If 2 variables(x/z coord) are changed, I draw a new circle every second to the new position accordingly - a timer runs in the background, invoking this method.
void DrawEllipse()
{
// Retrieve the image.
bChamber = new Bitmap(global::Project.Properties.Resources.driveChamber1);
gChamber = Graphics.FromImage(bChamber);
gChamber.FillEllipse(brushChamber, VirtualViewX(), VirtualViewY(), 10, 10);
pictureBoxDriveView.Image = bChamber;
}
Now I'm looking for ways to optimize the performance. Redrawing the pic every 0.2s e.g. slows the program so much, I cant do anything else.
But ultimately I need a more fluent movement of the circle, you can Imagine how it laggs with the 1000ms refresh rate.
Is there a better way to do this, then loading the whole bitmap every time?
Use the Controls the way they were intended.
do not redraw the Bitmap yourself.
just load it 1x in the Picturebox.
handle the Paint event of the picturebox to draw the ellipse
invalidate the Picturebox whenever your coords change.
Draw the circle ONE time in a control (PictureBox)
Put the control across the 300x300 picture box.
When, and only when, the variables change, update the location of the picturebox with the circle.
This way you prevent drawing too many times.
Try setting the DoubleBuffered property of the form to true. This generally results in improved performance.
Also, you should put this
// Retrieve the image.
bChamber = new Bitmap(global::Project.Properties.Resources.driveChamber1);
In the class constructor.
Try this, it does not load the image from disk every time, so it is less expensive.
private Image _origImage = new Bitmap(global::Project.Properties.Resources.driveChamber1);
void DrawEllipse()
{
// Retrieve the image.
Image bChamber = new Bitmap((Image)this._origImage.Clone());
Graphics gChamber = Graphics.FromImage(bChamber);
gChamber.FillEllipse(brushChamber, VirtualViewX(), VirtualViewY(), 10, 10);
pictureBoxDriveView.Image = bChamber;
}
I am building a C# app that shows the trajectory of a cannonball. But when I minimize it the trajectory I have drawn to a picture box are gone when I bring it back up. Is there an easy way to stop this?
I bet your drawing in the mouse event. Use the onpaint event and you should be good to go.
Edit:
Here is a decent drawing tutorial using the onpaint() event:
http://www.geekpedia.com/tutorial50_Drawing-with-Csharp.html
When the window is restored it will need to redraw the form. If you do not have your drawing as part of the paint event, then it will not be redrawn.
This question is very similar to this one
Saving a Graphics content to a file
As the others have already stated the problem is when you draw onto a graphics object, there is nothing retained. It is called persistent graphics. Sometimes you want this behavior, more often than not you don't.
You should do your drawing onto a bitmap, then copy the bitmap to your picturebox.Image. The other option as stated in the other answers, is do your drawing routines in the OnPaint Method.
Read my answer in the above. The title is misleading, he thought he had to save to a file to gain persistence, but we showed him otherwise.
EDIT Here is the important code from the above link
Bitmap buffer;
public Form1()
{
InitializeComponent();
panel1.BorderStyle = BorderStyle.FixedSingle;
buffer = new Bitmap(panel1.Width,panel1.Height);
//Make sure you resize your buffer whenever the panel1 resizes.
}
private void button1_Click(object sender, EventArgs e)
{
using (Graphics g = Graphics.FromImage(buffer))
{
g.DrawRectangle(Pens.Red, 100, 100,100,100);
}
panel1.BackgroundImage = buffer;
}
i was having same problem just used mainp.refresh() after event change
mainp was my panel in which i was drawing my all graphics
I've implemented a simple multithreaded Server\Client game as an assignment for my college.
on the client side in addition to the main thread there are:
1-thread which is responsible of drawing the play ground and the players on the form.
2-thread to communicate with the server to send the directions and receive the location and other information.
first I used the invoke technique but it didn't work because I was using the Graphics after it disposed. see Draw on a form by a separate thread
so In order to avoid that and regularize the drawing thread, I just raising the flag 'Invalidate' every specific time on the drawing thread and leave the actual handling of it to the main thread:
public Form1()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
InitializeComponent();
}
private void Draw()
{
while (true)
{
Thread.Sleep(200);
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (map.hasGraph)
{
map.Draw(e.Graphics);
if (this.Index != null)
e.Graphics.FillRectangle(Brush, Rectangle);
if (OpponentIndex != null)
e.Graphics.FillRectangle(OpponentBrush, OpponentRectangle);
}
}
the problem here that the form is blinking in arbitrary fashion even though I'm using double buffering, and the blinking is reduced when I increase the sleeping time for the drawing thread, but I think 200ms is already too much.
any advice?
[Edit]
I realized that I'm setting the double buffering flag from the code and from the property editor which made the problem (this may be a fool idea) but I spent half an hour testing my code with one of the flags and with both of them, the problem raised when I set the double buffering flag from two places, my problem is solved but please now I need to know if this could be what solved it.
It must get worse and worse the longer it runs right?
Everytime your program paints it launches draw, which has an infinite loop, which calls paint, which calls draw in another infinite loop. IT seems you have a circular reference here. If I can assume Map.Draw is private void Draw()
There is a far easier solution to this, draw everything to a bitmap then draw the bitpmap in the onPaint event.
Bitmap buffer=new Bitmap(this.Width, this.Height); //make sure to resize the bitmap during the Form.Onresize event
Form1()
{
InitializeComponent();
Timer timer=new Timer();
timer.Interval= 100;
timer.Tick+=......
timer.Start()
}
//the Timer tick event
private void timer_tick(....
{
if (map.hasGraph)
{
using (Graphics g = Graphics.FromImage(buffer))
{
//You might need to clear the Bitmap first, and apply a backfround color or image
//change color to whatever you want, or don't use this line at all, I don't know
g.Clear(Color.AliceBlue);
if (this.Index != null)
g.FillRectangle(Brush, Rectangle);
if (OpponentIndex != null)
g.FillRectangle(OpponentBrush, OpponentRectangle);
}
panel1.BackgroundImage=buffer;
}
}
Note I did not test this for syntax accuracy.
The memory of the system might be quite low for the operation executed.
read more about it.
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx
Create an image which will be used to store last rendered scene.
Create new thread whith will draw to the image
Create a timer whitch will refresh image
Copy image to form on timer tick