Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I have to demonstrate a use of Lambdas/Actions/Delegates within my program for my A-Level to make it more complex.
I know how to make them all and (kind of) what they do, but not when to use them.
Can anybody let me know where in my code I can use one, two or all of these? I wasn't sure if this was the right place for this, let me know if not.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//Command-Line Arguements for SizeX and SizeY
namespace prjT02L08_Predator_Prey
{
public partial class frmSim : Form
{
Point GridSize; //Creates a pair of X and Y co-ordinates
Random r = new Random(); //Used for any random number needed in the program
float FoodSpawnChance = 50; //The chance that food has of spawning
int SimTick = 0; //How long the program has been running for
int ImageScale = 7; //How much to scale the bitmap grid up for displaying
int SizeX = 60; //The number of pixels on the X axis
int SizeY = 60; //The number of pixels on the Y axis
bool StepDone = true; //Has one update of the simulation happened?
public frmSim() //Main constructor method
{
InitializeComponent();
GridSize = new Point(SizeX, SizeY); //Sets the size of the grid using the size of the X and Y axis
AStar.Grid = Node.MakeGrid(GridSize.X, GridSize.Y, 10);
for (int i = 0; i < 10; i++)
new Mitosis(r.Next(0, SizeX) /*Produces a random number between 0 and the size of the X axis*/,
r.Next(0, SizeY)/*Random number between 0 and the size of the Y axis*/); //Adds new Mitosis bacteria for the first time
for (int i = 0; i < 8; i++)
new Meiosis(r.Next(0, SizeX), r.Next(0, SizeY)); //Adds new Meiosis bacteria for the first time
chkNaturalSpawn.Checked = true; //Sets the food's natural spawn to true, so it randomly spawns
}
private void UpdateSim() //Updates the whole simulation
{
UpdateVars(); //Updates all the variables in the simulation
if (SimTick == 20) //If the simulation has run for 20 ticks,
for (int i = 0; i < 10; i++)
new VirusP(1, 1); //Creates a new virus at the X position 1, and Y position 1
if (chkNaturalSpawn.Checked == true) //If natural spawning has been selected
SpawnFood(); //then try to spawn food
Entity.UpdateAll(SimTick); //Updates all entities
}
private void UpdateVars() //Updates all the variables in the simulation
{
SimTick++; //Each timer tick, this variable is incremented
tmrSimClock.Interval = trcInterval.Value; //The gap between ticks is set based of the trackbar (Which has been reversed right-to-left)
if (chkNaturalSpawn.Checked == true) //Checks if natural food spawning is enabled
FoodSpawnChance = trcFoodSpawn.Value; //then if true, sets the chance of food spawning to the value of the trackbar
VirusP.DoubleStepChance = trcPred2Step.Value; //The chance of the Virus moving two places instead of one is set of the trackbar
}
private void SpawnFood() //Attempts to spawn food at a random location
{
//Chance to spawn based on FoodSpawnChance variable
if (r.Next(0, 1000) < (FoodSpawnChance * 100)) //Uses a random number to determine whether food can spawn or not
{
int x = r.Next(0, GridSize.X); //Sets the value of x to a random number between 0 and the value of the Gridsize.X
int y = r.Next(0, GridSize.Y); //Sets the value of y to a random number between 0 and the value of the Gridsize.Y
if (!AStar.Grid[x, y].IsWall) //Checks if the random position chosen isn't a wall
new Food(x, y); //then if true, food is spawned at that position
}
}
private void frmSim_Load(object sender, EventArgs e)
{
}
private void btnStep_Click(object sender, EventArgs e)
{
if (StepDone == true) //Checks if the previous update from this button has already been completed or not - Prevents slow down
{
StepDone = false;
UpdateSim(); //Updates the simulation once
DrawSim(); //Redraws the bitmap image to show a visual update
StepDone = true;
}
}
private void DrawSim() //Creates the bitmap of the grid which is dispalyed on the screen and scales it up
{
Bitmap bmp = new Bitmap(GridSize.X, GridSize.Y); //Creates the bitmap specifying the width and height of it
//These two for loops loop through every part of the grid:
for (int x = 0; x < GridSize.X; x++) //For every value in the height of the grid
{
for (int y = 0; y < GridSize.Y; y++)//and every value in the width of the grid
{
Color Colour = Color.Black; //Creates a new color used to set the pixel colour on the bitmap (Empty space is black)
foreach (Entity e in Entity.GetEntitiesAt(x, y)) //For every entity a the current location...
{
if ((e as Food) != null) //If it is Food, set the colour to green
Colour = Color.FromArgb(Colour.R, 255, Colour.B);
else if ((e as Mitosis) != null) //If it is bacteria Mitosis, set the colour to blue
Colour = Color.FromArgb(Colour.R, Colour.G, 255);
else if ((e as Meiosis) != null) //If it is bacteria Meiosis, set the colour to gold
Colour = Color.Gold;
else //If it's none of these, the only entity left is the Virus, set the colour to red
Colour = Color.FromArgb(255, Colour.G, Colour.B);
}
if (AStar.Grid[x, y].IsWall) //If that location is a wall, set the colour to white
Colour = Color.White;
bmp.SetPixel(x, y, Colour); //Set the pixel at position x and y to the colour chosen above
}
}
//Scales up the bitmap into a new bitmap
Bitmap bmpscale = new Bitmap(GridSize.X * ImageScale, GridSize.Y * ImageScale);
for (int x = 0; x < GridSize.X; x++)
{
for (int y = 0; y < GridSize.Y; y++)
{
for (int sx = 0; sx < ImageScale; sx++)
{
for (int sy = 0; sy < ImageScale; sy++)
{
bmpscale.SetPixel(((x * ImageScale) + sx), ((y * ImageScale) + sy), bmp.GetPixel(x, y));
}
}
}
}
this.CreateGraphics().DrawImage(bmpscale, new Point(10, 10)); //Draws the bitmap image at set co-ordinates on the form
}
private void tmrSimClock_Tick(object sender, EventArgs e) //Every time the timer updates
{
UpdateSim(); //Updates the simulation
DrawSim(); //Redraws the simulation
}
private void btnRun_Click(object sender, EventArgs e)
{
tmrSimClock.Enabled = !tmrSimClock.Enabled; //Put timer in opposite state
btnStep.Enabled = !btnStep.Enabled; //Put button in opposite state
if (tmrSimClock.Enabled)
btnRun.Text = "Running...";
else
btnRun.Text = "Run";
}
private void btnReset_Click(object sender, EventArgs e)
{
Entity.Entities = new List<Entity>(); //Recreates the list of entitites
SimTick = 0; //Restarts the simulation timer
tmrSimClock.Enabled = false; //Turns the timer off
for (int i = 0; i < 10; i++)//Recreates entities Mitosis and Meiosis
{
new Mitosis(r.Next(0, SizeX), r.Next(0, SizeY));
new Meiosis(r.Next(0, SizeX), r.Next(0, SizeY));
}
btnRun.Text = "Run";
}
private void chkNaturalSpawn_CheckedChanged(object sender, EventArgs e)
{
lblFood.Enabled = chkNaturalSpawn.Checked == true ? true : false; //Turnery Statement
trcFoodSpawn.Enabled = chkNaturalSpawn.Checked == true ? true : false; //If checked is true, return true, else return false
}
}
}
First, I've got to agree with Jon: make it tidy first. This is an example of lambda use by using an action as an anonymous delegate/lambda function. You may have to fiddle to get it to work
private void traverseBmp(Action<int, int> doIt)
{
for (int x = 0; x < GridSize.X; x++) //For every value in the height of the grid
{
for (int y = 0; y < GridSize.Y; y++)//and every value in the width of the grid
{
doIt(x, y);
}
}
private void DrawSim() //Creates the bitmap of the grid which is dispalyed on the screen and scales it up
{
Bitmap bmp = new Bitmap(GridSize.X, GridSize.Y); //Creates the bitmap specifying the width and height of it
//These two for loops loop through every part of the grid:
traverseBmp((x, y) =>
{
Color Colour = Color.Black;
//Creates a new color used to set the pixel colour on the bitmap (Empty space is black)
foreach (Entity e in Entity.GetEntitiesAt(x, y)) //For every entity a the current location...
{
if ((e as Food) != null) //If it is Food, set the colour to green
Colour = Color.FromArgb(Colour.R, 255, Colour.B);
else if ((e as Mitosis) != null) //If it is bacteria Mitosis, set the colour to blue
Colour = Color.FromArgb(Colour.R, Colour.G, 255);
else if ((e as Meiosis) != null) //If it is bacteria Meiosis, set the colour to gold
Colour = Color.Gold;
else //If it's none of these, the only entity left is the Virus, set the colour to red
Colour = Color.FromArgb(255, Colour.G, Colour.B);
}
if (AStar.Grid[x, y].IsWall) //If that location is a wall, set the colour to white
Colour = Color.White;
bmp.SetPixel(x, y, Colour); //Set the pixel at position x and y to the colour chosen above
});
//Scales up the bitmap into a new bitmap
Bitmap bmpscale = new Bitmap(GridSize.X * ImageScale, GridSize.Y * ImageScale);
traverseBmp((x,y) =>
{
for (int sx = 0; sx < ImageScale; sx++)
{
for (int sy = 0; sy < ImageScale; sy++)
{
bmpscale.SetPixel(((x*ImageScale) + sx), ((y*ImageScale) + sy), bmp.GetPixel(x, y));
}
}
});
this.CreateGraphics().DrawImage(bmpscale, new Point(10, 10)); //Draws the bitmap image at set co-ordinates on the form
}
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 have a function in C# which compare two images and finds the differences between them. The problem is that it is too slow. How can I optimize the code by using bytes instead of GetPixel() method?
The speed of this calculus is 0.34 seconds and I should do it in at least ten times less. Some colleague told me I can improve the speed by using bytes.
private void btnGo_Click(object sender, EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
Application.DoEvents();
// Load the images.
Bitmap bm1 = (Bitmap)picImage1.Image;
Bitmap bm2 = (Bitmap)picImage2.Image;
// Make a difference image.
int wid = Math.Min(bm1.Width, bm2.Width);
int hgt = Math.Min(bm1.Height, bm2.Height);
Bitmap bm3 = new Bitmap(wid, hgt);
// Create the difference image.
bool are_identical = true;
Color eq_color = Color.White;
Color ne_color = Color.Red;
for (int x = 0; x < wid; x++)
{
for (int y = 0; y < hgt; y++)
{
if (bm1.GetPixel(x, y).Equals(bm2.GetPixel(x, y)))
bm3.SetPixel(x, y, eq_color);
else
{
bm3.SetPixel(x, y, ne_color);
are_identical = false;
}
}
}
// Display the result.
picResult.Image = bm3;
this.Cursor = Cursors.Default;
if ((bm1.Width != bm2.Width) || (bm1.Height != bm2.Height))
are_identical = false;
if (are_identical)
lblResult.Text = "The images are identical";
else
lblResult.Text = "The images are different";
}
By using bytes I should be able to have this computation in 0.04 seconds.
I would like to be able to create X x Y number of boxes / circles / buttons inside a picturebox. EXACTLY like Windows Defrag tool.
I tried to create a layout and keep adding buttons or pictureboxes to it, but its extremely slow and after 200 or so pictureboxes it crashes, runs out of window handles or memory.
What's the alternative? Can you show me a simple piece of code to add boxes just like Defrag tool where I can easily access them like
box[x,y].Color = Green as my app makes progress?
Currently I have this:
private void ResetTableStyles()
{
boardPanel.Controls.Clear();
boardPanel.RowStyles.Clear();
boardPanel.ColumnStyles.Clear();
boardPanel.RowCount = Rows;
boardPanel.ColumnCount = Columns;
for (int i = 0; i < Rows; i++)
{
boardPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
}
for (int j = 0; j < Columns; j++)
{
boardPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
}
}
private void CreateButtons()
{
for (int i = 0; i < Rows; i++)
{
for (int j = 0; j < Columns; j++)
{
var button = new PictureBox
{
BackColor = Color.White,
Dock = DockStyle.Fill,
Margin = Padding.Empty,
Tag = new Point(i, j),
BackgroundImageLayout = ImageLayout.Stretch
};
//button.MouseDown += button_MouseDown;
boardPanel.Controls.Add(button, j, i);
}
}
}
which as I said doesn't work, after sometime it just crashes and it takes very long time.
Perhaps someone has a better answer, but you could use a Panel as the canvas and handle the Paint event to draw colored rectangles onto the Panel. You could then use the mouse events, such as MouseMove, to figure out which cell you're on.
Here is a super-simple proof of concept.
// Data.cs
namespace WindowsFormsApplication
{
public class Data
{
public int State { get; set; }
public string Tip { get; set; }
}
}
// Form1.cs
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var rng = new Random();
_data = new Data[30 * 30];
for (int i = 0; i < _data.Length; i++)
{
_data[i] = new Data
{
State = rng.Next(0, 3),
Tip = $"Data at index: {i}"
};
}
}
private Data[] _data;
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (var brush = new SolidBrush(Color.Gray))
using (var buffer = BufferedGraphicsManager.Current.Allocate(e.Graphics,
new Rectangle(0, 0, 450, 450)))
{
for (int x = 0; x < 30; x++)
{
for (int y = 0; y < 30; y++)
{
int dataIdx = (y * 30) + x;
Data data = _data[dataIdx];
if (data.State == 1)
{
brush.Color = Color.Blue;
}
else if (data.State == 2)
{
brush.Color = Color.Red;
}
else
{
brush.Color = Color.Gray;
}
buffer.Graphics.FillRectangle(brush, x * 15, y * 15, 15, 15);
buffer.Graphics.DrawLine(Pens.Black, 0, y * 15, 450, y * 15); //Gridline
}
buffer.Graphics.DrawLine(Pens.Black, x * 15, 0, x * 15, 450); //Gridline
}
buffer.Render(e.Graphics);
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
var point = e.Location;
int x = point.X / 15;
int y = point.Y / 15;
int dataIdx = (y * 30) + x;
System.Diagnostics.Debug.WriteLine(_data[dataIdx].Tip);
}
}
}
The Data class represents the model behind each segment on the panel and is pretty straight forward.
Form1 has a single control placed on it: a Panel named panel1 with a size of 450 x 450. Each cell on the panel will be 15 x 15, so we have 30 columns and 30 rows. In Form1's constructor, we initialize the Data that will be used to draw the panel.
Inside of Form1.panel1_Paint we iterate through the cells and look up the instance of Data for the given cell. Then, based on the State, set a color for the cell and draw a rectangle of that color. This is done on a buffer so the panel doesn't flicker when painting.
Inside of Form1.panel1_MouseMove we use the location of the mouse pointer relative to panel1 to figure out which cell the mouse is over, get the instance of Data for that cell, and display the Tip in the debug window.
You might be able to take this and build upon it, perhaps with a custom UserControl so you can easily access each individual cell with the column and row... your box[x, y].Color = Colors.Green example above.
I am attempting to draw about 3600 points on a form, it is pretty slow using one thread so I decided I want to use 4 threads for it.
In my code I divide the 3600 points to the 4 threads and they are supposed to draw it. however for some reason an ArgumentOutOfRangeException is being thrown.
I tried to debug my code but I couldn't find the mistake.
here is the code :
(Ignore the class _3DPoint, it is just a point that has x,y,z values. when I draw them I only use the x,y values.)
code for drawing the points :
public Graphics g; //g = this.CreateGraphics() in form1.Load()
public void drawrectangle(_3DPoint)
float xCord = float.Parse(p.x.ToString());
float yCord = float.Parse(p.y.ToString());
Brush b = new SolidBrush(Color.White);
xCord = lsize * xCord + center.X;
yCord = lsize * yCord + 10 + center.Y;
g.FillRectangle(b, xCord, yCord, 2, 2);
}
lsize, center are just variables for aligning the points as I want them.
All of the multithread action code:
public List<_3DPoint[]> multiThreadsdata = new List<_3DPoint[]>();
public void handlemultithread(_3DPoint[] P)
{
g.Clear(Color.Black);
for (int i = 0; i < multiThreads.Length; i++)
{
multiThreadsdata.Add(new _3DPoint[P.Length / multiThreads.Length]);
}
for (int i = 0; i < multiThreads.Length; i++)
{
for (int j = (P.Length / multiThreads.Length) * (i); j < (P.Length / multiThreads.Length) * (i + 1); j++)
{
multiThreadsdata[i][j - ((P.Length / multiThreads.Length) * i)] = new _3DPoint(P[j]);
}
}
for (int i = 0; i < multiThreads.Length; i++)
{
multiThreads[i] = new Thread(() => drawPoints(multiThreadsdata[i]));
multiThreads[i].Start();
}
}
delegate void SetCallBackPoint(_3DPoint location);
public void drawPoints(_3DPoint[] locations)
{
for (int i = 0; i < locations.Length; i++)
{
if (this.InvokeRequired)
{
SetCallBackPoint e = new SetCallBackPoint(drawrectangle);
this.Invoke(e, new object[] { locations[i] });
}
else
{
drawrectangle(locations[i]);
}
}
}
P is a _3DPoint array that contains all the 3600 points.
mutliThreads is a Thread[] containing 4 threads.
I get the exception in handlemultithread method. in the third line of this for loop :
for (int i = 0; i < multiThreads.Length; i++)
{
multiThreads[i] = new Thread(() => drawPoints(multiThreadsdata[i])); // <- here.
multiThreads[i].Start();
}
I don't know what is the problem, my guess is that there is some problem with the multithreading because I'm just a beginner with multithreading.
Thanks a bunch.
It is entirely possible to Draw 3600 rectangles quickly on a form when you apply the suggestions in the comments.
If that doesn't give you enough time you can consider creating Images on a single background thread, store them in some sort of buffer until they are needed to e painted on the Graphics object of the Paint event of the form. That is only feasible if you can know upfront what needs to be painted on the next frame.
This example uses a simple Background worker to fill an ConcurrentQueue with images. The comments in the code explain what is going on.
public partial class Form1 : Form
{
static ConcurrentQueue<Image> buffer = new ConcurrentQueue<Image>();
static Random r = new Random();
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
// this is already a great performance win ...
DoubleBuffered = true;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Image img =null;
// get from buffer ..
if (!buffer.TryDequeue(out img))
{
// nothing available
// direct random
for (var x = 0; x < e.ClipRectangle.Width; x++)
{
for (var y = 0; y < e.ClipRectangle.Height; y++)
{
using (var pen = new Pen(new SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))))
{
e.Graphics.DrawRectangle(pen, x, y, 1, 1);
}
}
}
}
else
{
// otherwise Draw the prepared image
e.Graphics.DrawImage(img,0,0);
Trace.WriteLine(buffer.Count);
img.Dispose();
}
}
private void button1_Click(object sender, EventArgs e)
{
// force a repaint of the Form
Invalidate();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// as long as the form is not disposed
while (!IsDisposed)
{
// we keep 60 images in memory
if (buffer.Count < 60)
{
// bitmap
var bmp = new Bitmap(this.Width, this.Height);
var img = Graphics.FromImage(bmp);
// draw
for (int i = 0; i < 3600; i++)
{
using (var pen = new Pen(new SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))))
{
img.DrawRectangle(pen, r.Next(Width),r.Next(Height), r.Next(Width), r.Next(Height));
}
}
// store the drawing in the buffer
buffer.Enqueue(bmp);
}
else
{
// simple and naive way to give other threads a bit of room
Thread.Sleep(0);
}
}
}
}
Keep in mind that when you have a CPU heavy process adding more threads will not by magic make your methods run quicker. You might even make it worse: more threads compete for time on the CPU.
What would be the best way to draw 25 rectangles (5*5) in c#?
I later need to be able to reach a specific rectangle and change its color, for instance change the color to red if the user inputs the incorrect word.
Would it be more suitable to create an array of rectangles in this case?
This is what i have so far
Graphics g = pictureBox1.CreateGraphics();
int x =0;
int y= 0;
int width = 20;
int height = 20;
for (int i = 0; i < 25; i++)
{
if (i <= 4)
{
g.FillRectangle(Brushes.Blue, x, y, width, height);
x += 50;
}
else if (i > 4)
{
y = 50;
g.FillRectangle(Brushes.Blue, x, y, width, height);
x += 50;
}
}
This should get you started, not the complete code. You will need to add a PictureBox control and use the default name (picurebox1). EDIT: Need to add a button too :)
public partial class Form1 : Form
{
public List<Rectangle> listRec = new List<Rectangle>();
Graphics g;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Rectangle rect = new Rectangle();
rect.Size = new Size(100,20);
for (int x = 0; x < 5; x++)
{
rect.X = x * rect.Width;
for (int y = 0; y < 5; y++)
{
rect.Y = y * rect.Height;
listRec.Add(rect);
}
}
foreach (Rectangle rec in listRec)
{
g = pictureBox1.CreateGraphics();
Pen p = new Pen(Color.Blue);
g.DrawRectangle(p, rec);
}
}
public void ChangeColor(Rectangle target, Color targetColor)
{
Pen p = new Pen(targetColor);
g.DrawRectangle(p, target.X, target.Y, target.Width, target.Height);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.D0: ChangeColor(listRec[0], Color.Red);
break;
case Keys.D1: ChangeColor(listRec[1], Color.Red);
break;
//..more code to handle all keys..
}
}
}
If you're not concerned with performance or the looks, then the easiest approach would be to create a List of Lists of Panels in your Form_Load event as one of the comments mentions.
List<List<Panel>> panelGrid = new List<List<Panel>>();
for (var i = 0; i < 5; i++)
{
var panelRow = new List<Panel>();
for (var j = 0; j < 5; j++)
{
panelRow.Add(new Panel());
// add positioning logic here
}
panelGrid.Add(panelRow);
}
Then you will be able to reference each individual one at a later stage...
If you have to use Graphics class (which is the better approach), then you should setup something similar however replacing Panel with a class of your own. Then in Form_Paint event you would iterate through the list of objects and render them.
class MyPanel
{
public Size size;
public Color color;
}
...
foreach (var myPanelRow in myPanelGrid)
foreach (var myPanel in myPanelRow)
g.FillRectangle(myPanel.color, myPanel.size); // this obviously won't work as is, but you get the idea
Then when you need to change a color, you do something like:
myPanelsGrid[0][0].color = Color.Blue;
myForm.Invalidate();
The second line will result in Paint in event being called again.