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
Related
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.
i have a class that draws waveforms of audio. I'm drawing it in OnPaint function. Now i need to draw a line that shows where on the waveform we are at current moment. I can calculate the position but when i try to draw it i need to call Invalidate() and that forces form to redraw all that waveform chart data (a lot of points).
So is there a way to put some transparent element over this one and then call Invalidate() only on that element? i was trying with picture box but no sucess...
//main form
private void timer100ms_Tick(object sender, EventArgs e)
{
customWaveViewer1.currentPosition = (int)((stream.Position / (float)stream.Length) * customWaveViewer1.Width);
customWaveViewer1.overlayLabel.Invalidate(false);
}
//drawing function in my class
private void overlayLabelInvalidate(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0));
e.Graphics.DrawLine(pen, currentPosition, 0, currentPosition, this.Height);
}
//constructor
public CustomWaveViewer()
{
InitializeComponent();
this.DoubleBuffered = true;
this.PenColor = Color.DodgerBlue;
this.PenWidth = 1;
overlayLabel = new PictureBox();
overlayLabel.Size = new System.Drawing.Size(this.Width, this.Height);
overlayLabel.Location = new Point(this.Left, this.Top);
overlayLabel.Visible = true;
overlayLabel.BackColor=Color.FromArgb(0,0,0,0);
overlayLabel.Paint += new System.Windows.Forms.PaintEventHandler(this.overlayLabelInvalidate);
Controls.Add(overlayLabel);
}
Actually what you are saying is not exactly true.
In the painteventargs there is a rectangle indicating the small portion of the window that needs to be repainted.
Also when you invalidate, you don't necessarily need to invalidate the whole form.
In your case you might want to invalidate only the old and new position of the marker that indicates where you are in the waveform.
So in your algorithm of your paint method, it is really up to you to make it efficient and only paint that part of the window that really needs repainting and to skip the part that does not need repainting.
It really can make a huge difference.
To make it even more look professional, set double buffering on.
No need to hastle with bitmaps of the whole image you have yourself, that is just what double buffering is all about, and forms can do it for you.
I copied following excerpt from https://msdn.microsoft.com/en-us/library/windows/desktop/dd145137(v=vs.85).aspx
BeginPaint fills a PAINTSTRUCT structure with information such as the dimensions of the portion of the window to be updated and a flag indicating whether the window background has been drawn. The application can use this information to optimize drawing. For example, it can use the dimensions of the update region, specified by the rcPaint member, to limit drawing to only those portions of the window that need updating. If an application has very simple output, it can ignore the update region and draw in the entire window, relying on the system to discard (clip) any unneeded output. Because the system clips drawing that extends outside the clipping region, only drawing that is in the update region is visible.
In this case there is no simple output and taking this into account is adviced.
I am not saying that creating a bitmap will not work. I am saying that optimizing your drawing logic will solve it just as well.
The information above still stands as windows forms is built on top of the old win32 api.
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 have an application where the user draws some shapes.
When I click over a shape and I drag it, the CPU goes 100% because of Invalidate() inside MouseMove.
If I a use a timer and call Invalidate() from tick event the moving is not so smooth.
Is there any other approach to minimize CPU and have smooth moving?
` Point startDragMousePoint;
Point startShapeLocation;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if(isMouseDown)
{
Point deltaPoint = Point.Subtract(e.Location, new Size(startDragMousePoint));
shape.Location = Point.Add(startShapeLocation, new Size(deltaPoint));
Invalidate();
}
}
private void Canvas_Paint(object sender, PaintEventArgs e)
{
shape.Render(e.Graphics);
}`
There are three general solutions.
1) Don't draw while your moving, this was the solution in windows for a long time, when you dragged a window, it just disapeard and you saw the outline of a window.
2) Create a bitmap object and only move that. Note you will have to redraw the area under it.
3) Don't invalidate the hole window, just the area you are changing. Drawing to a buffer (a bitmap) can help you reuse areas.
Also, if GDI isn't the fastest drawing functions in the world. If your shape is very complex, you might want to consider using OpenGL, DirectX or SDL.
Instead of invalidating the entire area you could invalidate the portion of the control that has changed by using:
Rectangle changedArea = new Rectangle(cX, cY, cW, cH);
this.Invalidate(changedArea);
Also make sure your control is set to use DoubleBuffering
this.DoubleBuffered = true;
From the limited code that you have put up, I think the invalidate will not cause any problem. Most probably the problem may be inside the real rendering code of yours shape.Render(). In the past I have written similar application where I have called Invalidate in the mouse move and the applications has worked fine. Only there were some flickering which is gone on enabling double buffering.
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.