I wrote a program that draws a specific situation of a board game(I think it's called "Ludo" in English). Now I can draw all the fields and the pawns etc., but if a pawn is moved I have to redraw that pawn. The problem is that the screen flashes for a very short time when I do this. This is probably because I first clear all fields and pawns, and then redraw them(I'm going to improve this, but the problem will, in smaller form, still occur I think), the screen is then flashing between the time I cleared everything until I redrawed everything. Is there a way to tell C# not to redraw even when I specifically call something like this.Controls.Clear() until I tell it to?
I already tried it with this.SuspendLayout(), because the name suggests it should do exactly this, but that doesn't work.
Flickering is always going to be somewhat of a problem in Winforms custom graphics.
Try this: Instead of drawing the pawn's positions on the Form somewhere, draw them onto a Bitmap. When it's all done, simply change the Image of a PictureBox on your form to be the Bitmap. The form will be redrawn using the new Image. You can use two Bitmap objects (which are admittedly quite large) and clean them each time to avoid the program being a memory hog. Voila, you now have "page-flipping" graphics, which can be drawn and displayed independently of other requests to redraw the form.
When you tell it to clear everything and then redraw it, it will flash like that. But how come when you move a pawn you don't draw it in the new space and draw a blank over top of where it used to be. This will move the pawn without refreshing the whole screen or clearing everything.
Also I think the game you are talking about is Chess. Thats the only game I know with Pawns, and I've never heard of Ludo. :P
Best solution is the one of KeithS, but a faster option could be: have two images for each part, one on the white and one on the black, plus both empty square. In the move place the proper empty square in the leaving square, and the proper part on gthe destination square. This is a zero flicker, old ages ( so fast ) approach :)
Although #KeightS's solution is nice, I'd much rather solve it by being able to suspend/resume drawing all together:
public partial class Form1 : Form
{
private bool suspendDraw;
public Form1()
{
this.InitializeComponent();
}
public void SuspendDraw()
{
this.suspendDraw = true;
}
public void ResumeDraw()
{
this.suspendDraw = false;
}
private const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
if ((m.Msg != WM_PAINT) || (!this.suspendDraw && m.Msg == WM_PAINT))
{
base.WndProc(ref m);
}
}
}
You just need to call the SuspendDraw/ResumeDraw() methods to control allowing painting.
Related
I was doing a game project in Windows Forms and really loved how it turned out, except for one thing that was bugging me: the new picturebox's I am adding are "eating" away from the one behind it, showind the background of its parent and not showing the image behind him, as I thought it will. Apparently that's how transparency works in Windows Forms, it copies the colors behind him, basically.
This is how it looks, and I want the animals to be seen fully.
I also tried this from another post here, but it turned out like this.
There might be no solution to this, I have other things in this little game I made. There is another picturebox with other buttons and stuff, that represents the shop. And also you can see in both images that there is a pannel in the bottom section with some details. In that case, I would leave it as it is and maybe try another time to move it to WPF.
=================== EDIT ===================
The accepted answer helped me switch from a game with overlaying PictureBoxes to a game where I "paint" each frame of the game on the background. Check that answer's comment for more details about this :) This is how it turned out.
This is specifically for my code, where I have a static Resources class. Yours could look a lot cleaner, maybe you have this Render function where you have every other rectangle and image. I hope this helps everyone that visits this page :)
// ================ SOLUTION ================
public static void Render()
{
//draw the background again. This is efficient enough, maybe because the pixels that did not changed won't be redrawn
grp.DrawImage(Resources.gameBackground, 0, 0);
//draw the squirrel image on the position and length of the "squirrel" Rectangle
grp.DrawImage(Resources.currentSquirrelImage, Resources.squirrel.X, Resources.squirrel.Y, Resources.squirrel.Width, Resources.squirrel.Height);
//after that, draw each projectile (acorns, wallnuts) the same way
foreach (Projectile projectile in Resources.projectiles)
{
grp.DrawImage(projectile.image, projectile.rect.X, projectile.rect.Y, projectile.rect.Width, projectile.rect.Height);
}
//then draw each animal
foreach (Enemy animal in Resources.enemies)
{
grp.DrawImage(animal.image, animal.rect.X, animal.rect.Y, animal.rect.Width, animal.rect.Height);
}
//and finally, the image that shows where the squirrel is shooting
grp.DrawImage(Resources.selectionImge, Resources.selection.X, Resources.selection.Y, Resources.Selection.Width, Resources.Selection.Height);
//update the image of the game picturebox
form.TheGame.Image = bmp;
}
As you have noticed .net control transparency is not real transparency, it copies it's parent background, so if you have other sibiling controls the one with the higher Z index will occlude the others.
If you want to create a game avoid the usage of picture boxes, there are many options: use a game engine like Unity or roll your own.
Something easy to do is to create a Bitmap, render your game in it and then present it in your form, but beware, that can be slow.
EDIT: As you requested here is an example on how to use the Intersect function of the Rectangle struct to determine which parts of two rectangles overlap.
Rectangle R1 = new Rectangle (0,0,32,32);
Rectangle R2 = new Rectangle (16,16,32,32);
//To test if a rectangle intersects with another...
bool intersects = R1.IntersectsWith(R2); //If does not intersect then there's nothing to update
//To determine the area that two rectangles intersect
Rectangle intersection = Rectangle.Intersect(R1, R2); //In this example that would return a rectangle with (16,16,16,16).
TL;DR: Look at the picture below
So I'm trying to make a little picture, and I and people around me are kinda out of ideas.
I have a table (the sitting+eating one) in the middle (seen from above), and people sitting around it. Those people are round, as isthe table.
Every person has their own picturebox, I just use one picture, rotate it, and set it as image in the next box.
Thep roblem now is: The PictureBoxes of people on corners overlap the table with empty corner, in the image there is transparency there. It should show the table below it, but instead it shows the background of the Form :(
Edit: All backgrounds are set to transparent, the Form has the marble as background and white ("Window") as background colour.
I put one person in the back and one in the front, so it's easy to see:
Edit 2 (same as ocmment):
In the last two days I read this question about 10 times, and not one that described this exact problem has had an actual answer. When trying to push one of those, I was told I should post a new question.
Example: How to make picturebox transparent?
Transparency in winforms is kind of misleading, since it's not really transparency.
Winforms controls mimic transparency by painting the part of their parent control they would hide instead of their own background.
However, they will not paint the other controls that might be partially covered by them.
This is the reason your top most picture boxes hides your big picture box.
You can overcome this by creating a custom control that inherits from PictureBox and override its OnPaintBackground method (taken, with slight adjustments, from this code project article):
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
Graphics g = e.Graphics;
if (this.Parent != null)
{
var index = Parent.Controls.GetChildIndex(this);
for (var i = Parent.Controls.Count - 1; i > index; i--)
{
var c = Parent.Controls[i];
if (c.Bounds.IntersectsWith(Bounds) && c.Visible)
{
using (var bmp = new Bitmap(c.Width, c.Height, g))
{
c.DrawToBitmap(bmp, c.ClientRectangle);
g.TranslateTransform(c.Left - Left, c.Top - Top);
g.DrawImageUnscaled(bmp, Point.Empty);
g.TranslateTransform(Left - c.Left, Top - c.Top);
}
}
}
}
}
Microsoft have published a Knowledge base article to solve this problem a long time ago, however it's a bit out-dated and it's code sample is in VB.Net.
Another option is to paint the images yourself, without picture boxes to hold them, by using Graphics.DrawImage method.
The best place to do it is probably in the OnPaint method of the form.
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.
TL;DR: Look at the picture below
So I'm trying to make a little picture, and I and people around me are kinda out of ideas.
I have a table (the sitting+eating one) in the middle (seen from above), and people sitting around it. Those people are round, as isthe table.
Every person has their own picturebox, I just use one picture, rotate it, and set it as image in the next box.
Thep roblem now is: The PictureBoxes of people on corners overlap the table with empty corner, in the image there is transparency there. It should show the table below it, but instead it shows the background of the Form :(
Edit: All backgrounds are set to transparent, the Form has the marble as background and white ("Window") as background colour.
I put one person in the back and one in the front, so it's easy to see:
Edit 2 (same as ocmment):
In the last two days I read this question about 10 times, and not one that described this exact problem has had an actual answer. When trying to push one of those, I was told I should post a new question.
Example: How to make picturebox transparent?
Transparency in winforms is kind of misleading, since it's not really transparency.
Winforms controls mimic transparency by painting the part of their parent control they would hide instead of their own background.
However, they will not paint the other controls that might be partially covered by them.
This is the reason your top most picture boxes hides your big picture box.
You can overcome this by creating a custom control that inherits from PictureBox and override its OnPaintBackground method (taken, with slight adjustments, from this code project article):
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
Graphics g = e.Graphics;
if (this.Parent != null)
{
var index = Parent.Controls.GetChildIndex(this);
for (var i = Parent.Controls.Count - 1; i > index; i--)
{
var c = Parent.Controls[i];
if (c.Bounds.IntersectsWith(Bounds) && c.Visible)
{
using (var bmp = new Bitmap(c.Width, c.Height, g))
{
c.DrawToBitmap(bmp, c.ClientRectangle);
g.TranslateTransform(c.Left - Left, c.Top - Top);
g.DrawImageUnscaled(bmp, Point.Empty);
g.TranslateTransform(Left - c.Left, Top - c.Top);
}
}
}
}
}
Microsoft have published a Knowledge base article to solve this problem a long time ago, however it's a bit out-dated and it's code sample is in VB.Net.
Another option is to paint the images yourself, without picture boxes to hold them, by using Graphics.DrawImage method.
The best place to do it is probably in the OnPaint method of the form.
I want to to make a shared drawing board in C#. This means that two people connected via a TCP connection can draw on this board. The idea (for now) is that people can click on the screen and draw. What do you think is the best method for this?
It's easy enough to draw a dot when the user clicks on a certain spot, but it gets a little more complicated when the user drags the mouse, where you need to draw a line between the last point and the current. Also that doesn't work so well, so I draw a dot where the line starts to improve things a bit, but it's not that good.
Lastly, I need to also send this over TCP, so I need to distinguish between the two. I hoped that I could just send points and have it draw it on the other side, but it seems it wouldn't work. Any ideas except also sending the type?
drawing http://img193.imageshack.us/img193/9697/drawingw.png
EDIT:
ok, I'm going with a IDrawingArgument interface that has Dispatch(myForm), and basically does double dispatch, so it solves the TCP problem (going to serialize/deserialize it).
Lines are still a bit bulky.
One little tip... on your mousemove event. keep a flag that wont fire the event again until the last event that set the flag turns it off. i.e.:
bool isDrawing = false;
public void myCanvas_MouseMove(object sender, EventArgs e)
{
if(!isDrawing)
{
isDrawing = true;
// Do drawing here
isDrawing = false;
}
}
This helped me a lot when doing drawing in a mousemove event.
Dots:
(x,y),(x2,y2),(x3,y3)
Lines:
(x,y,x2,y2),(x3,y3,x4,y4)
Thus, the format is a list of tuples. Tuples of size 4 are lines, of size 2 are points. Note that if your system gets more complicated, you'll really regret not just doing something like:
Dots:
D(x,y),D(x2,y2),D(x3,y3)
Lines:
L(x,y,x2,y2),L(x3,y3,x4,y4)