How can I instantiate large number of buttons in Windows Forms? - c#

I'm developing a theatre reservation software. I'm using Windows Forms, the seats is represented by a 2-dimensioned array. And I draw the buttons as following:
public void DrawSeats()
{
// pnl_seats is a Panel
pnl_seats.Controls.Clear();
// Here I store all Buttons instance, to later add all buttons in one call (AddRange) to the Panel
var btns = new List<Control>();
// Suspend layout to avoid undesired Redraw/Refresh
this.SuspendLayout();
for (int y = 0; y < _seatZone.VerticalSize; y++)
{
for (int x = 0; x < _seatZone.HorizontalSize; x++)
{
// Check if this seat exists
if (IsException(x, y))
continue;
// Construct the button with desired properties. SeatSize is a common value for every button
var btn = new Button
{
Width = SeatSize,
Height = SeatSize,
Left = (x * SeatSize),
Top = (y * SeatSize),
Text = y + "" + x,
Tag = y + ";" + x, // When the button clicks, the purpose of this is to remember which seat this button is.
Font = new Font(new FontFamily("Microsoft Sans Serif"), 6.5f)
};
// Check if it is already reserved
if (ExistsReservation(x, y))
btn.Enabled = false;
else
btn.Click += btn_seat_Click; // Add click event
btns.Add(btn);
}
}
// As said before, add all buttons in one call
pnl_seats.Controls.AddRange(btns.ToArray());
// Resume the layout
this.ResumeLayout();
}
But already with a seat zone of 20 by 20 (400 buttons), it spent almost 1 minute to draw it, and in debug I checked that the lack of performance, is the instantiation of the buttons.
There is a way to make it faster? Perhaps disable all events during the instatiation or another lightweight Control that has the Click event too?
UPDATE:
lbl was a test, the correct is btn, sorry.
UPDATE 2:
Here is the IsException and ExistsReservations methods:
private bool IsException(int x, int y)
{
for (var k = 0; k < _seatsExceptions.GetLength(0); k++)
if (_seatsExceptions[k, 0] == x && _seatsExceptions[k, 1] == y)
return true;
return false;
}
private bool ExistsReservation(int x, int y)
{
for (var k = 0; k < _seatsReservations.GetLength(0); k++)
if (_seatsReservations[k, 0] == x && _seatsReservations[k, 1] == y)
return true;
return false;
}

Suppose that you change your arrays for reservations and exclusions to
public List<string> _seatsExceptions = new List<string>();
public List<string> _seatsReservations = new List<string>();
you add your exclusions and reservations in the list with something like
_seatsExceptions.Add("1;10");
_seatsExceptions.Add("4;19");
_seatsReservations.Add("2;5");
_seatsReservations.Add("5;5");
your checks for exclusions and reservations could be changed to
bool IsException(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsExceptions.Contains(key);
}
bool ExistsReservation(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsReservations.Contains(key);
}
of course I don't know if you are able to make this change or not in your program. However consider to change the search on your array sooner or later.
EDIT I have made some tests, and while a virtual grid of 20x20 buttons works acceptably well (31 millisecs 0.775ms on average), a bigger one slows down noticeably. At 200x50 the timing jumps to 10 seconds (1,0675 on average). So perhaps a different approach is needed. A bound DataGridView could be a simpler solution and will be relatively easy to handle.

I also won't use such a myriad of controls to implement such a thing. Instead you should maybe create your own UserControl, which will paint all the seats as images and reacts on a click event.
To make it a little easier for you i created such a simple UserControl, that will draw all the seats and reacts on a mouse click for changing of the state. Here it is:
public enum SeatState
{
Empty,
Selected,
Full
}
public partial class Seats : UserControl
{
private int _Columns;
private int _Rows;
private List<List<SeatState>> _SeatStates;
public Seats()
{
InitializeComponent();
this.DoubleBuffered = true;
_SeatStates = new List<List<SeatState>>();
_Rows = 40;
_Columns = 40;
ReDimSeatStates();
MouseUp += OnMouseUp;
Paint += OnPaint;
Resize += OnResize;
}
public int Columns
{
get { return _Columns; }
set
{
_Columns = Math.Min(1, value);
ReDimSeatStates();
}
}
public int Rows
{
get { return _Rows; }
set
{
_Rows = Math.Min(1, value);
ReDimSeatStates();
}
}
private Image GetPictureForSeat(int row, int column)
{
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
return Properties.Resources.emptySeat;
case SeatState.Selected:
return Properties.Resources.choosenSeat;
default:
case SeatState.Full:
return Properties.Resources.fullSeat;
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
var row = (int)(e.X / widthPerSeat);
var column = (int)(e.Y / heightPerSeat);
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
_SeatStates[row][column] = SeatState.Selected;
break;
case SeatState.Selected:
_SeatStates[row][column] = SeatState.Empty;
break;
}
Invalidate();
}
private void OnPaint(object sender, PaintEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
for (int row = 0; row < Rows; row++)
{
for (int column = 0; column < Columns; column++)
{
var seatImage = GetPictureForSeat(row, column);
e.Graphics.DrawImage(seatImage, row * widthPerSeat, column * heightPerSeat, widthPerSeat, heightPerSeat);
}
}
}
private void OnResize(object sender, System.EventArgs e)
{
Invalidate();
}
private void ReDimSeatStates()
{
while (_SeatStates.Count < Rows)
_SeatStates.Add(new List<SeatState>());
if (_SeatStates.First().Count < Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count < Columns)
columnList.Add(SeatState.Empty);
while (_SeatStates.Count > Rows)
_SeatStates.RemoveAt(_SeatStates.Count - 1);
if (_SeatStates.First().Count > Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count > Columns)
columnList.RemoveAt(columnList.Count - 1);
}
}
This will currently draw forty rows and columns (so there are 800 seats) and you can click on each seat to change its state.
Here are the images i used:
EmtpySeat:
ChoosenSeat:
FullSeat:
If you anchor this control and resize it or you click on a seat to change its state there can be some minor lacking for the repainting if you further increase the number of rows or columns, but that is still somewhere far below one second. If this still hurts you, you have to improve the paint method and maybe check the ClipRectangle property of the paint event and only paint the really needed parts, but that's another story.

Rather than using actual button controls, just draw the image of the seats then when the user clicks on a seat translate the mouse X,Y coordinates to determine which seat was clicked. This will be more efficient. Of course, the drawback is that you have to write the method to translate x,y coordinates to a seat, but that really isn't that difficult.

EDIT; it has been pointed out to me this will not work in Windows Forms!
Well, you are Sequentially working through it.
if one iteration costs 1 sec, the full process will take 400*1 in time.
Perhaps you should try and make a collection of your objects, and process it 'parallel'.
try the .Net framework (4 and above) 'parallel foreach' method:
http://msdn.microsoft.com/en-s/library/system.threading.tasks.parallel.foreach(v=vs.110).aspx
edit: so, if you have a list buttonnames, you can say
buttonNames.ForEach(x=>CreateButton(x));
while your CreateButton() method is as following:
private Button CreateButton(string nameOfButton) { Button b = new
Button(); b.Text = nameOfButton; //do whatever you want...
return b; }

Related

Conway's Game of Life not updating correctly [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I wrote a quick implementation of Conway's Game of Life, but it ran awfully slow mostly because my method of checking for neighbouring cells involved looping through the entire grid of cells again, now I've changed my method of checking for neighbouring cells but unfortunately it's not updating correctly anymore, it seems to work fine except that it doesn't create nearly as many new cells as it should.
Now I have spent a good few hours manually debugging the code, going through it with breakpoints and trying to compare the values and calls, but it SEEMS as if my GetNeighbours() method is working, so I concede to you, I can't figure out what's wrong on my own. I've submitted the code below for help.
EDIT: Some of you have pointed out that I can't copy my Grid.cells array the way I am doing it. I've changed it to use Array.Copy() instead but unfortunately it still doesn't work completely. I can't figure it out but it still doesn't seem to create new cells in all cases where it should.
MainForm.cs
public partial class MainFom : Form
{
Grid formGrid;
CancellationTokenSource tokenSrc = new CancellationTokenSource();
public MainFom()
{
InitializeComponent();
}
private void MainFom_Load(object sender, EventArgs e)
{
formGrid = new Grid();
for (int i = 0; i < 50; i++)
{
int xCoord = 10 * i + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = i.ToString(),
Location = new Point(xCoord, 0),
Font = new Font(Font.FontFamily, 6)
});
for (int s = 0; s < 50; s++)
{
int yCoord = 10 * s + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = s.ToString(),
Location = new Point(0, yCoord),
Font = new Font(Font.FontFamily, 6)
});
}
}
}
private void MainFom_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(formGrid.toBitmap(), 0, 0);
e.Graphics.Dispose();
}
private void startBtn_Click(object sender, EventArgs e)
{
Task tempTask = Task.Factory.StartNew(
(x) =>
{
while (!tokenSrc.IsCancellationRequested)
{
formGrid.UpdateGrid();
Graphics graphics = this.CreateGraphics();
graphics.Clear(this.BackColor);
graphics.DrawImage(formGrid.toBitmap(), 0, 0);
graphics.Dispose();
}
}, tokenSrc);
startBtn.Hide();
Button stopBtn = new Button() { Text = "Stop", Location = startBtn.Location, Size = startBtn.Size };
this.Controls.Add(stopBtn);
stopBtn.Click += new EventHandler(
(x, y) =>
{
tokenSrc.Cancel();
stopBtn.Hide();
startBtn.Show();
tempTask.Wait();
tokenSrc = new CancellationTokenSource();
});
}
}
Grid.cs
class Grid
{
#region Properties/Fields
const int MAX_CELLS = 50;
Random RNG = new Random();
Cell[,] cells;
int generations = new int();
#endregion
public Grid()
{
cells = new Cell[MAX_CELLS, MAX_CELLS];
for (int x = 0; x < MAX_CELLS; x++)
{
int xCoord = 10 * x + 12;
for (int y = 0; y < MAX_CELLS; y++)
{
int yCoord = 10 * y + 12;
Point point = new Point(xCoord, yCoord);
if (RNG.Next(100) < 20) {
cells[x, y] = new Cell(point, true); }
else {
cells[x, y] = new Cell(point, false);
}
}
}
}
public void UpdateGrid()
{
Cell[,] copy = cells;
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
int neighboursCtr = GetNeighbours(x, y);
//Rule 1: Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if (cells[x, y].IsAlive && neighboursCtr < 2)
{
copy[x, y].Kill();
}
//Rule 2: Any live cell with more than three live neighbours dies, as if by overcrowding.
if (cells[x, y].IsAlive && neighboursCtr > 3)
{
copy[x, y].Kill();
}
//Rule 3: Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (!cells[x, y].IsAlive && neighboursCtr == 3)
{
copy[x, y].Alive();
}
}
}
cells = copy;
generations++;
}
public Bitmap toBitmap()
{
Bitmap gridBmp = new Bitmap(1000, 1000); // TODO: Find optimal size for bmp
Size cellSize = new Size(10, 10);
using (Graphics gfxObj = Graphics.FromImage(gridBmp))
{
// Draw grid here and Dispose() on Pen, gfxObj is implicitly disposed
Pen myPen = new Pen(Color.LightGray);
SolidBrush myBrush = new SolidBrush(Color.Black);
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
if (!cells[x, y].IsAlive)
{
gfxObj.DrawRectangle(myPen, new Rectangle(cells[x, y].point, cellSize));
} else
{
gfxObj.FillRectangle(myBrush, new Rectangle(cells[x, y].point, cellSize));
}
}
}
myPen.Dispose();
myBrush.Dispose();
}
return gridBmp;
}
private int GetNeighbours(int column, int row)
{
int neighbours = new int();
int[] starts = new int[] { Math.Max(0 ,column - 1), Math.Max(0, row - 1) };
int[] ends = new int[] { Math.Min(49, column + 1), Math.Min(49, row + 1) };
double colAndRow = column + row/10;
for (int x = starts[0]; x < ends[0]+1; x++)
{
for (int y = starts[1]; y < ends[1]+1; y++)
{
double xAndY = x + y/10;
if (cells[x, y].IsAlive && xAndY != colAndRow)
{
neighbours++;
}
}
}
return neighbours;
}
}
Cell.cs
struct Cell
{
public bool IsAlive { get; private set; }
public readonly Point point;
public Cell(Point point, bool isAlive) : this()
{
this.point = point;
IsAlive = isAlive;
}
public void Alive()
{
IsAlive = true;
}
public void Kill()
{
IsAlive = false;
}
}
The problem is in your UpdateGrid() method. You're simply assigning the reference for your original array to a new variable:
Cell[,] copy = cells;
But this is still the same object; in particular, there's no difference between calling copy[x, y].Kill() and cells[x, y].Kill(). So you're modifying your state during calculations, this affects your code's logic.
Make a copy of the original using Array.Copy and it should work correctly (there doesn't seem to be anything else wrong with your algorithm).
Arrays are reference types, which means
Cell[,] copy = cells;
doesn't what you probably intent to do. It's not a copy of the source array, so it will manipulate this whilst analyzing the neighbors which will lead to wrong results.
Use Array.Copy.
There are a lot of improvements that can be done.
Take a look at Optimizing Conway's 'Game of Life' and Hashlife
You can start by using LockBits to work faster with the bitmap.
You can use parallel programming to improve the loops: Save time with parallel FOR loop
You can also improve the algorithm avoiding the whole matrix scan each time, and instead maintain a list of the alive cells, and only step thru these cells and it's neighbors.
I've implemented such algorithm in the following C# Game of Life Code.

Label text not updating

public void ItemGot()
{
number = number + 1; //Increment by 1
quantity.Text = ("x" + number); //Overwrite Text
quantity.Refresh(); //Updates the text
}
Hello, I have this code above. When this method runs, the text of a label I set up earlier should change the text to the one i set below. However, its not doing it. Furthermore, by setting breakpoints in Visual Studio, I have determined that:
The method is being called,
Number is being incremented properly.
There should be NO reason why this not working, because the program is recognizing that number is increasing by one. My friend said a similar question here. Still no help, and now that code is outdated. Please help!
EDIT: how I added the quantity label
First, I initialized it in the constructor: public Label quantity;
Then I did this: quantity = new Label();
Lastly, in another method, I gave the quantity the following properties:
quantity.Size = new Size(24, 24);
quantity.Text = ("x" + number);
quantity.Left = 48;
Controls.Add(quantity);
number is also in the constructor and is set to 0.
EDIT 2 : I'll Post my whole method
public InventoryScreen()
{
btnItems = new Button();
quantity = new Label();
//call the methods for spawning the buttons
ButtonGenItems(cNumber, btnItems, quantity);
InitializeComponent();
}
public void InventoryScreen_Load(object sender, EventArgs e)
{
}
#region ButtonGenItems Method
public void ButtonGenItems(int cNumber, Button btnItems,Label quantity)
{
int xPos = 126;
int yPos = 25;
for (int n = 0; n < 1; n++)
{
btnItems.Tag = n;
btnItems.Size = new Size(48, 52); //Button size X and Y
btnItems.BackColor = Color.CornflowerBlue;
quantity.Size = new Size(24, 24);
quantity.Text = ("x" + number);
if (yPos > 60) // Five Buttons in one column
{
yPos = 25; //spawn position Y
xPos = xPos + btnItems.Width + 10; //spacing X
}
btnItems.Left = xPos; //Start Button spawn at the Left side
btnItems.Top = yPos; //Start spawn at the top side
quantity.Left = 48;
quantity.Top = 60;
yPos = yPos + btnItems.Height + 10;
btnItems.Text = "Use";
Controls.Add(btnItems); //place Buttons
Controls.Add(quantity);
// the Event of click Button
//btnItems.Click += new System.EventHandler(ItemUse); //to be implimented
}
}
#endregion
public void ItemGot()
{
//*Interestingly, the program recognizes that 'number' is increasing by 1, but label won't update the text
//Furthermore, pressing the actual button will trigger the text update, but simulating a buttonclick WONT DO ANYTHING
Console.WriteLine("Text should now increment by 1"); //Debugging to test method
number = number + 1; //Increment by 1
quantity.Text = ("x" + number); //Overwrite Text
}
}
2.5 This is how the method is being called. This method is located in another class
public void Update(Vector2 pos)
{
this.position = pos; //get char position
Inv = new InventoryScreen(); //create instance of object
charRange = new Rectangle((int)position.X, (int)position.Y, 64, 57); //create rectangle
//Intersection Code, If the character intersects with the item while the item is showing, run below
if (alive && charRange.Intersects(itemRect))
{
alive = false; //stop showing the item
Inv.ItemGot(); //Call the ItemGot class, which adds the item to the inventory screen
}
}
As I understand you have already open/showed form(instance of class InventoryScreen) with your label when you calling Update method, But...
Inside of method Update you creating a new instance of InventoryScreen, and calling function ItemGot with this new instance of form.
I think you need to pass reference of your current instance of InventoryScreen in method Update, then use that reference for calling ItemGot method
public void Update(Vector2 pos, InventoryScreen invscreen)
{
this.position = pos;
charRange = new Rectangle((int)position.X, (int)position.Y, 64, 57);
if (alive && charRange.Intersects(itemRect))
{
alive = false;
invscreen.ItemGot();
}
}

Events with lambda expression

I need someone to help me with this little problem I have. Here's a code fragment:
void shuffle()
{
for (int i = 0; i < 4; i++) {
// here should be the computations of x and y
buttons[i].Click += (s, e) => { show_coordinates(x, y); };
// shuffling going on
}
}
void show_coordinates(int x, int y)
{
MessageBox.Show(x + " " + y);
}
as you can see I create a new event handler with different x and y for each button every time I run the loop. There's another button in my form which shuffles the buttons randomly.
So here's the problem: If I press the shuffle button say 10 times and then press any of the shuffled buttons, event handlers stack up and I get 10 message boxes displaying x and y values.
So how can I overwrite the previous event handler with a new one each time I press shuffle.
Thanks in advance.
I would redesign the code instead to do something like this:
private PointF[] points = new PointF[4];
//Run once
public void Initialize()
{
for (int i = 0; i < 4; i++)
buttons[i].Click += (s, e) => { show_coordinates(i); };
}
public void Shuffle()
{
for (int i = 0; i < 4; i++)
{
// here should be the computations of x and y
points[i] = new PointF(x,y);
// shuffling going on
}
}
public void show_coordinates(int index)
{
var point = points[index];
MessageBox.Show(point.X + " " + point.Y);
}
Reflection is a one way to do it, but i would prefer a delegate and adding/removing that delegate. This leads to easier maintainable code than using a reflection:
How to remove a lambda event handler
In your for loop first remove the event handlers then re-add them.
However take into account that this is bad practice. You should use the button's datacontext and bind an object to it containing your x and y values.
This way you only have to attach your event handler once. Instead of every time shuffle get's called. Then updating your buttons datacontext is way more convenient.
Example PseudoCode:
public MainWindow() {
ForEach Button addEvent(DoSomething);
ForEach Button Button.Datacontext = new Data();
}
public class Data {
prop X;
prop Y;
ctor(x,y)
ctor()
}
public void DoSomething()
{
var data = Button.datacontext as data
MessageBox(data.x, data.y)
}
public void Shuffle()
{
calc x, y
foreach Button (Button.datacontext as data).x = x, ...
}

Drag one picturebox to picturebox, it drags them all

When i drag one picturebox, it drags both of mine. Because in the pbxMap_DragDrop method i have to call both of the methods that should fire when i drag one.
private void pbxMap_DragDrop(object sender, DragEventArgs e)
{
myDetectMouse.setMinotaur(e, myMap.myCells);
myDetectMouse.setTheseus(e, myMap.myCells);
}
SetTheseus:
public void setTheseus(DragEventArgs e, List<Cell> cells)
{
for (int i = 0; i < cells.Count; i++)
{
int[] mapData = myMapController.getMapData(i, cells);
int column = mapData[0];
int row = mapData[1];
int right = mapData[2];
int bottom = mapData[3];
Point RelativeMouseLoc = myMapController.myMap.myForm.pbxMap.PointToClient(Cursor.Position);
if (RelativeMouseLoc.X > column &&
RelativeMouseLoc.X < column + myMapController.myMap.myCellSize
&& RelativeMouseLoc.Y > row && RelativeMouseLoc.Y <
row + myMapController.myMap.myCellSize)
{
myMapController.myMap.myCells[i].hasTheseus = true;
}
else
{
myMapController.myMap.myCells[i].hasTheseus = false;
}
}
}
SetMinotaur is much the same but replace hasTheseus with hasMinotaur. As soon as a cell "hasTheseus" or "hasMinotaur" it will be drawn to the cell.
So it draws them both when i drag one because they both get set in pbxMap_DragDrop.
I thought i could have multiple event handlers for pbxMap_DragDrop depending on which picturebox was dragged.
You can check the sender parameter to determine whether you want to move the minotaur or Theseus. It would look something like this:
var pic = (PictureBox)sender;
if (pic.Name == "minotaur")
{
myDetectMouse.setMinotaur(e, myMap.myCells);
}
else
{
myDetectMouse.setTheseus(e, myMap.myCells);
}
If you don't want to use the Name property, you can use something else like the Tag property - just make sure you set it for each of the PictureBox objects.

checking a color of a button and changing it using a 2D array

I'm creating a small game just like the game Reversi/Othello I have managed to created a 2x3 board with buttons.
The buttons change colour ones you click on them but I'm having trouble to detect if there is a white colour in between 2 black colours and if so change that white colour into black.. I hope this make sense. the buttons are in a 2D array. Any suggestions that could help me do this would be much appreciated.
The image:
Here is my code:
![namespace reversitest
{
public partial class Form1 : Form
{
private Button\[,\] squares;
public Form1()
{
InitializeComponent();
squares = new Button\[3, 2\];
squares = new Button\[,\] {{button1, button2, button3},
{button4, button5, button6,}};
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (Button sqrr in squares)
{
sqrr.Click += new System.EventHandler(this.DrawCharacter);
}
}
int _turn = 0;
private void DrawCharacter(object sender, EventArgs e)
{
Button sqrr = (Button)sender;
int col = 0;
if (sqrr.BackColor.Equals(Color.Black) || sqrr.BackColor.Equals(Color.White))
{
MessageBox.Show("Move Not Allowed!");
}
else
{
for ( int i = 0; i < squares.GetLongLength(1); ++i)
{
// check othere squares and change color
if (i < 2)
{
for (int f = 0; f < 3; ++f)
{
var ss = squares\[i, f\];
if (ss.BackColor.Equals(Color.Black))
{
MessageBox.Show("we have a black");
//ss = squares\[i, f+1\];
ss.BackColor = Color.Black;
}
else
{
MessageBox.Show("no black");
}
}
}
if (_turn == 0)
{
_turn = 1;
sqrr.BackColor = Color.Black;
}
else
{
_turn = 0;
sqrr.BackColor = Color.White;
}
}
}
}
}
}
First name your buttons with the array index. It will help you to find the button.
For example according to you picture button1 name would be btn_1_1.
Then inside your button click event first get the button name and then identify the button positioned.
Button b = sender as Button;
string[] btnData = b.Name.Split('_');
int x = int.Parse(btnData[1]);
int y = int.Parse(btnData[2]);
//check for possible combinations
int top = y - 2;
int botton = y + 2;
int left = x - 2;
int right = x + 2;
if (top >= 0 && squares[top, y].Background == Color.Black)
{
squares[top+1, y].Background = Color.Black;
}
...
...
Continue like that. If you need more detail please free to ask.
Final Answer
//check for possible combinations
int top = x - 2;
int botton = x + 2;
int left = y - 2;
int right = y + 2;
if (top >= 0 && squares[top, y].BackColor == Color.Black)
{
squares[top + 1, y].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
Will be extended for a 8x8 board later on
Do you need it to be elegant? A kind of brute force method: You could check for pieces in the 8 different directions it is possible for them to be aligned. So for example, you start with a black piece. Check the next piece over in one direction. If it's white, keep going and take a note of the position that was white so you can change it to black later. When you finally hit a black piece, change all the stored positions to black and move on to the next direction and repeat the process until you've done all 8 directions.

Categories