I am creating Conway's Game of life in C# and everything works fine, except the refreshing of the panel to display the next generation. I can draw to the panel to establish grid lines for the cells, place and destroy "life" using simWindow_Paint and simWindow_MouseClick but at present i cannot update it.
private void updateGame(){
int living = 0;
int dead = 0;
for(int i = 1; i < gameHeight; i++)
for (int j = 1; j < gameWidth; j++){
//set cell location and size
Rectangle cell = new Rectangle();
cell.Height = cell.Width = 9;
int X = j - 1;
int Y = i - 1;
cell.X = Convert.ToInt32(X * 10 + 1);
cell.Y = Convert.ToInt32(Y * 10 + 1);
using (Graphics g = this.simWindow.CreateGraphics()){
if (gameArray[i, j] == true){
Brush brush = new SolidBrush(Color.Red);
g.FillRectangle(brush, cell);
++living;
} else {
Brush brush = new SolidBrush(Color.White);
g.FillRectangle(brush, cell);
++dead;
}
}
}
}
I am not sure if what i am attempting to do is possible in C#, I have already done this in Pascal, but i am not sure if C# works the same way...
Difficult to say with what you've shown us, but I suspect the problem might be in your simWindow_Paint method. This is (presumably) responding to the paint event for your panel and I suspect what you are doing is overwriting (or over painting) all the drawing you are doing in your updateGame() method.
All the drawing should be done in your paint handler. So you paint handler should know the state of the game and draw it accordingly. Your updateGame() method should just update the state of the game and then invalidate the panel (forcing a repaint).
I recommend you review the following documentation on custom painting in WinForms: http://msdn.microsoft.com/en-us/library/kxys6ytf.aspx
Related
I am in the process of creating an escape room game in c#. It is made up of a 10x10 2D array of the class "Tiles" (with a rectangle instance variable), which are drawn onto the form by a method of the "Room" class. There are three different methods involved in the construction of the Room: "buildRoom" (called within the constructer) which builds up the 2D array, "setPoints" which sets up the entry and exit points, and "drawRoom", which uses a graphics object to draw the rectangles on the form. I originally called these methods within Form1 after creating an object of room, but since I have copied this over into a new Form class it is no longer working. The program runs, adding pictures and accepting key presses as movements, but it just doesn't draw on the form. I've tried stepping through the program but everything looks as if it should work. Any help would be appreciated in figuring this out.
This is the code for setting up the Form, and calling the method to draw.
public void GameSetUp()
{
this.Height = 560;
this.Width = 535;
Room room1 = new Room();
room1.buildRoom();
room1.setPoints("room1");
this.Text = "Room 1";
//passing the form into the method
**room1.displayRoom(this);**
Character user = new Character();
StateManager.C = user;
StateManager.C.addCharacter(room1, this, "room1");
}
Code for setting up the 2D Array:
public void buildRoom()
{
int xPos = 10;
int yPos = 10;
int width = 50;
int height = 50;
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
Tile t = new Tile();
t.rect = new Rectangle(xPos, yPos, width, height);
Board[x, y] = t;
yPos += height;
}
xPos += width;
yPos = 10;
}
}
Code for drawing the rectangles onto the Form:
public void displayRoom(Form f)
{
Graphics g = f.CreateGraphics();
Pen p = new Pen(Brushes.Black);
p.Width = 2;
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
g.DrawRectangle(p, Board[x,y].rect);
if(Board[x, y].getEntry())
{
g.FillRectangle(Brushes.Green, Board[x, y].rect);
}
else if (Board[x, y].getExit())
{
g.FillRectangle(Brushes.Red, Board[x, y].rect);
}
else if (Board[x, y].getProblem())
{
g.FillRectangle(Brushes.SaddleBrown, Board[x, y].rect);
}
else
{
g.FillRectangle(Brushes.Bisque, Board[x, y].rect);
}
}
}
The problem seems not apparent from these code snippets. Try to debug your code. Maybe you are drawing to early, e.g. when the form is not yet visible?
Also, this is not the right way to draw in forms. It is the operating system (Windows), which decides when a form has to be drawn. E.g. when another form on top of yours is removed or when you restore the window from minimized state.
Therefore, you must override OnPaint and do the painting there. When you want to repaint, you can call Invalidate() (a method of a Form and of all controls) and let Windows decide when to repaint (i.e., when to call OnPaint) or call Refresh() to force an immediate repaint.
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e); // Call the OnPaint method of the base class.
Graphics g = e.Graphics; // Do not create your own Graphics object.
using Pen p = new Pen(Brushes.Black); // A using var statement or a plain
// using statement must the pen.
p.Width = 2;
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
g.DrawRectangle(p, Board[x,y].rect);
if(Board[x, y].getEntry())
{
g.FillRectangle(Brushes.Green, Board[x, y].rect);
}
else if (Board[x, y].getExit())
{
g.FillRectangle(Brushes.Red, Board[x, y].rect);
}
else if (Board[x, y].getProblem())
{
g.FillRectangle(Brushes.SaddleBrown, Board[x, y].rect);
}
else
{
g.FillRectangle(Brushes.Bisque, Board[x, y].rect);
}
}
}
}
Btw.: Your code looks Java-ish. In C# one would use PascalCase for methods and properties, where you would use the property syntax for properties instead of plain getXY and setXY methods.
See also:
Disposing GDI Objects in C#. NET.
Properties (C# Programming Guide)
C# Coding Standards and Naming Conventions
I am trying to add a picturebox control to a panel which is created at runtime.
It is for a chess game I am working on. I want to add a picture box to each panel , an assigning the image to the control later. Here is what I have so far:
//Sets the number of rows on the chess board
for (int i = 0; i < 8; i++)
{
//Set the number of columns on the board
for (int j = 0; j < 8; j++)
{
ChessSquare sq = new ChessSquare(((char)(65 + i)).ToString(), 7- j);
sq.Color = (i + (j % 2)) % 2 == 0 ? Color.Black : Color.White;
Panel p = new Panel()
{
Size = new Size(blockSize, blockSize),
BackColor = sq.Color,
Tag = sq,
Location = new Point(blockSize * i + 15, blockSize * j + 15),
BackgroundImageLayout = ImageLayout.Stretch
};
p.MouseEnter += new EventHandler(squareMouseEnter);
p.MouseLeave += new EventHandler(squareMouseLeave);
p.Click += new EventHandler(squareMouseClick);
chessBoardPanels[i, j] = p;
groupBox1.Controls.Add(p);
}
}
//SetUp Board
SetUpBoad Setup = new SetUpBoad();
SetUpBoad(chessBoardPanels);
Since you already put the Panels into the panel-array (?)
chessBoardPanels[i, j] = p;
You can add the PictureBoxes either now or later..:
PictureBox pb = new PictureBox ();
pb.Size = ..
pb.BackColor = Color.Transparent;
chessBoardPanels[i,j].Controls.Add(pb);
To access them later you can cast their first Control to PictureBox:
PictureBox pb = (PictureBox)chessBoardPanels[i,j].Controls[0];
pb.Image = aQueenImage;
If you want to add a PictureBox only where a piece is you need to do checks:
if (chessBoardPanels[i,j].Controls.Count > 0)
{
PictureBox pb = (PictureBox)chessBoardPanels[i,j].Controls[0];
pb.Image = aQueenImage;
}
To move a piece from <i1,j1> to <i2, j2> you do as expected:
chessBoardPanels[i1,j1].Controls[0].Parent = chessBoardPanels[i2,j2];
I notice that you are hooking up mouse events. If you want to use them to move the pieces, remember that transparency will not work for overlapping controls in Winforms and so while a piece-Box is crossing Panels it will not have working tranparency around the Image.
While the pBox is nested in a Panel all is well but to move it you would have to first make it a child of the parent of those panels and only add it to the target Panel upon MouseUp; the coordinate corrections can be solved but the tranparency, if you need it, will be a bigger problem..
The usual advice it to consider drawing at least those board squares and maybe even the pieces onto a base board-Panel (or board-PictureBox)
So I have my code drawing rectangles and circles inside a flowLayoutPanel.
Afterwards I've made it so that when you click inside of the flowLayoutPanel, the rectangle you selected is shown on your left on a 2nd flowLayoutPanel as shown in the following picture:
But the problem is that whenever any other position is clicked, ie [0,1] or [2,3], the selected one isn't shown. I also know that the array works properly because when I change the code flowLayoutPanel2.CreateGraphics(); to flowLayoutPanel1.CreateGraphics(); inside of the mouse click event it works, but it changes the rectangles in the main container.
The following are the two methods that mess around with it. Does anyone know what the problem is?
private void DrawIt()
{
System.Drawing.Graphics graphics = flowLayoutPanel1.CreateGraphics();
graphics.Clear(Form1.ActiveForm.BackColor);
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(50 * j, 50*i, 50, 50);
rectangleGrid[i, j] = rectangle;
graphics.DrawEllipse(System.Drawing.Pens.Black, rectangle);
graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle);
}
}
}
private void flowLayoutPanel1_MouseClick(object sender, MouseEventArgs e)
{
int rowIndex = e.Y / 50;
int columnIndex = e.X / 50;
System.Drawing.Graphics graphics = flowLayoutPanel2.CreateGraphics();
System.Drawing.Rectangle rectangle = rectangleGrid[rowIndex, columnIndex];
graphics.DrawEllipse(System.Drawing.Pens.BlueViolet, rectangle);
graphics.DrawRectangle(System.Drawing.Pens.Chartreuse, rectangle);
}
This code is just changing location, not sliding.
I am using this at the moment:
for (int i = 740; i == 740; i++)
{
panel2.Location = new Point(panel2.Location.X - i, panel2.Location.Y);
}
How can I slide the panel slowly?
Now, as I stated in my comment you really need to use float values, so you really need to draw it. However, the current implementation only makes a single iteration. The current loop could have been translated into this:
panel2.Location = new Point(panel2.Location.X - 740, panel2.Location.Y);
Consider a loop like this to slide it out:
for (int i = -(panel2.Width); i < 0; i++)
{
panel2.Location = new Point(i, panel2.Location.Y);
}
That algorithm is assuming that you've set the Location to the - of it's width (e.g. -740x) so that it's simply not visible on the screen. The reverse would hide it.
This will still be a little choppy, but it won't just hide it like your current code.
If you are just going to slide the panel try to do this
Try this code:
Panel panelArray = new Panel[];
Panel panel2 = panelArray[0];
for (int i = 0; i <= 100; i++)
{
panel2.Location = new Point(panel2.Location.X - i, panel2.Location.Y);
System.Threading.Thread.Sleep(10);
}
Im having a problem with setting custom border of Control. This border can be done with DrawRectangle, DrawBorder or anything else as long as I get this behaviour
Obviously, the darkest border is where previouse border was. Other borders are around it trying to mimic fade out(or whatever). Now, the most challenging thing is I cannot override OnPaint or extend any other Control. This HAS to work on all Controls!
This is part of my extender provider whith which I set these borders when control has focus(like Google Chrome).
So far I have come up with this...
When adding controls in extender provider dictionary I hook up on Enter and Leave events of control. In there I get the parent of the control that is firing the event and on that form I draw these 3 rectangles. That way I sorted painting on non client area. The thing that remains is painting the actual border of control. I have tried and tried but to no avail.
I also hooked up on paint event of that control but ControlPaint.DrawBorder() is not working.
Okay, so this is method that is getting called on Enter and leave.
private void BojajGlow(Graphics gfx, Graphics gfxCtrl, Control parent, Control kontrola, bool novi)
{
Rectangle[] rect = new Rectangle[3];
for (int i = 0; i < 3; i++)
{
int x = kontrola.Location.X - (i + 1);
int y = kontrola.Location.Y - (i + 1);
int w = kontrola.Size.Width + 2 * (i + 1) - 1;
int h = kontrola.Size.Height + 2 * (i + 1) - 1;
rect[i] = new Rectangle(x, y, w, h);
}
if (novi)
{
Color boja = DohvatiOpcije(kontrola).Boja;
for (int i = 0; i < 3; i++)
{
if (i > 0)
boja = Posvjetli(95, ControlPaint.Light(boja));
Pen olovka = new Pen(boja);
olovka.EndCap = olovka.StartCap = LineCap.Round;
olovka.Width = 1;
GraphicsPath gfxPath = new GraphicsPath();
gfxPath.AddRectangle(rect[i]);
gfx.DrawPath(olovka, gfxPath);
}
}
else
{
for (int i = 0; i < 3; i++)
{
Pen olovka = new Pen(parent.BackColor);
olovka.EndCap = olovka.StartCap = LineCap.Round;
olovka.Width = 1;
GraphicsPath gfxPath = new GraphicsPath();
gfxPath.AddRectangle(rect[i]);
gfx.DrawPath(olovka, gfxPath);
}
}
}
From Enter event it is going to be called like this
if (((Control)sender).Parent != null)
BojajGlow(Graphics.FromHwnd(((Control)sender).Parent.Handle), Graphics.FromHwnd(((Control)sender).Handle), ((Control)sender).Parent, (Control)sender, true);
Does anyone have any valuable input on this?
In winforms you are probably going to need to create your own custom control inheriting from the TextBox control. In your control you can implement the OnPaint based on the controls state such as whether or not it has focus.
As for drawing outside of the control, dont. It will only frustrate you. Instead draw three boarders using the forms background color WITHIN your control and change them to the hightlighted color when you need them to glow.
Hope this helps.