I have a TableLayoutPanel and I want to add a control to the cell that I click on.
The problem is that I can't determine the cell that I click on at run time.
How do I determine the cell being clicked on?
You can use GetColumnWidths and GetRowHeights methods to calculate the cell row and column index:
Point? GetRowColIndex(TableLayoutPanel tlp, Point point)
{
if (point.X > tlp.Width || point.Y > tlp.Height)
return null;
int w = tlp.Width;
int h = tlp.Height;
int[] widths = tlp.GetColumnWidths();
int i;
for (i = widths.Length - 1; i >= 0 && point.X < w; i--)
w -= widths[i];
int col = i + 1;
int[] heights = tlp.GetRowHeights();
for (i = heights.Length - 1; i >= 0 && point.Y < h; i--)
h -= heights[i];
int row = i + 1;
return new Point(col, row);
}
Usage:
private void tableLayoutPanel1_Click(object sender, EventArgs e)
{
var cellPos = GetRowColIndex(
tableLayoutPanel1,
tableLayoutPanel1.PointToClient(Cursor.Position));
}
But notice that the click event only is raised if the cell does not already contain a control.
This worked for me:
public TableLayoutPanel tableLayoutPanel { get; set; }
private void Form_Load(object sender, EventArgs e)
{
foreach (Panel space in this.tableLayoutPanel.Controls)
{
space.MouseClick += new MouseEventHandler(clickOnSpace);
}
}
public void clickOnSpace(object sender, MouseEventArgs e)
{
MessageBox.Show("Cell chosen: (" +
tableLayoutPanel.GetRow((Panel)sender) + ", " +
tableLayoutPanel.GetColumn((Panel)sender) + ")");
}
Note that my tableLayoutPanel is declared globally so that I can just use it without having to pass it to each function. Also, both the tableLayoutPanel and each Panel within it are created completely programatically elsewhere (my form [design] is completely blank).
My answer is based on #Mohammad Dehghan's answer above but has a couple of advantages:
It now takes into account vertical scrolling
The columns are in the correct order (starts at i=0 instead of i=length), meaning columns of different widths or heights are processed in the correct order
Here is the updated version of the code:
public Point? GetIndex(TableLayoutPanel tlp, Point point)
{
// Method adapted from: stackoverflow.com/a/15449969
if (point.X > tlp.Width || point.Y > tlp.Height)
return null;
int w = 0, h = 0;
int[] widths = tlp.GetColumnWidths(), heights = tlp.GetRowHeights();
int i;
for (i = 0; i < widths.Length && point.X > w; i++)
{
w += widths[i];
}
int col = i - 1;
for (i = 0; i < heights.Length && point.Y + tlp.VerticalScroll.Value > h; i++)
{
h += heights[i];
}
int row = i - 1;
return new Point(col, row);
}
Nick's answer was the best solution, except that it can be made generic for TableLayoutPanels that contain different kinds of controls in the cells. Just change the explicit "Panel" type to "Control":
public TableLayoutPanel tableLayoutPanel { get; set; }
private void Form_Load(object sender, EventArgs e)
{
foreach (Control c in this.tableLayoutPanel.Controls)
{
c.MouseClick += new MouseEventHandler(ClickOnTableLayoutPanel);
}
}
public void ClickOnTableLayoutPanel(object sender, MouseEventArgs e)
{
MessageBox.Show("Cell chosen: (" +
tableLayoutPanel.GetRow((Control)sender) + ", " +
tableLayoutPanel.GetColumn((Control)sender) + ")");
}
This works great and doesn't require doing coordinate math to find which cell was clicked.
Related
I am currently having trouble with my slide puzzle. The thing is that the codes work but only for the 9 buttons that appeared out of no where. Here is an image of it.
This is the code I used to run the program.
public partial class frmSlide : Form
{
/*
* frmSlide -constructor
*
*/
DateTime TimedSolved = DateTime.Now;
bool starting = false;
bool changed = false;
const int rows = 3;
const int cols = 3;
Button blkButton= new Button();
public Button[,] btnArray = new Button[rows, cols];
Button[,] buttons = new Button[3, 3];
public frmSlide()
{
InitializeComponent();
}
/*
* allNumberButton_Click - click event handler for allNumberButton
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void frmSlide_Load(object sender, EventArgs e)
{
blkButton.Text = " ";
int curNum = 1;
for (int x = 0; x < rows; x++)
{
for (int y = 0; y < cols; y++)
{
Button b;
b = new Button();
b.Location = new Point(100 + (y * 50), 100 + (x * 50));
b.Height = 40;
b.Width = 40;
b.Text = curNum.ToString();
btnArray[x, y] = b;
b.TextAlign = ContentAlignment.MiddleCenter;
b.Font = new Font(FontFamily.GenericSerif, (float)15.0);
b.Click += new System.EventHandler(this.allNumberButton_Click);
this.Controls.Add(b);
curNum++;
}
}
btnArray[rows - 1, cols - 1].Text = "";
blkButton = btnArray[rows - 1, cols - 1];
blkButton.Text = " ";
}
private void allNumberButton_Click(object sender, EventArgs e)
{
{
Button clickedButton = sender as Button;
if (clickedButton.Text == "")
{
goto done;
}
Point clicked = clickedButton.Location;
int clickedX = clicked.X;
int clickedY = clicked.Y;
Point blank = blkButton.Location;
int blankX = blank.X;
int blankY = blank.Y;
int distanceX = Math.Abs(clickedX - blankX);
int distanceY = Math.Abs(clickedY - blankY);
changed = false;
if ((distanceX == 50 && distanceY == 0) ||
(distanceX == 0 && distanceY == 50))
{
changed = true;
string tmp = clickedButton.Text;
clickedButton.Text = "";
blkButton.Text = tmp;
blkButton = clickedButton;
}
if (changed == true)
{
if (btnArray[rows - 1, cols - 1].Text != "")
{
goto done;
}
int checkNum = 1;
for (int a = 0; a < rows; a++)
{
for (int b = 0; b < cols; b++)
{
if (a == rows - 1 && b == cols - 1)
{
goto done2;
}
if (btnArray[a, b].Text != checkNum.ToString())
{
goto done;
}
checkNum++;
}
}
done2:;
if (starting == false)
{
DateTime nowSolved = DateTime.Now;
TimeSpan lengthOfTime = nowSolved - TimedSolved;
double hours = lengthOfTime.TotalHours;
int hours2 = Convert.ToInt32(hours);
double minutes = lengthOfTime.TotalMinutes;
int minutes2 = Convert.ToInt32(minutes);
double seconds = lengthOfTime.TotalSeconds;
int seconds2 = Convert.ToInt32(seconds);
MessageBox.Show("Congratulations! \n \n It took you " + hours2 +
" hours " + minutes2 + " minutes " + seconds2 + " seconds \n \n"
+ " to solve it!");
}
else
{
return;
}
}
done:;
}
}
/*
* btnReset_Click - click event handler
* .text
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnReset_Click(object sender, EventArgs e)
{
{
int reset = 1;
for (int c = 0; c < rows; c++)
{
for (int l = 0; l < cols; l++)
{
btnArray[c, l].Text = reset.ToString();
reset++;
btnArray[rows - 1, cols - 1].Text = "";
blkButton = btnArray[rows - 1, cols - 1];
}
}
}
}
/*
* btnExit_Click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
/*
* frmSlide_Load- Load event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnStart_Click(object sender, EventArgs e)
{
starting = true;
Random rnd = new Random();
for (int z = 0; z < 100; z++)
{
int rndNumberX = rnd.Next(3);
int rndNumberY = rnd.Next(3);
Button random = btnArray[rndNumberX, rndNumberY];
random.PerformClick();
}
starting = false;
}
}
Can you help me fix this problem with a solution because it is hard for me to figure out what happened?
I thought it might be the code I entered in the form load.
You'll need to remove the generation of those buttons in your frmSlide_Load event handler.
You'll also need to assign your existing buttons to btnArray.
I'll demonstrate how to do that using object initializer.
Comment out all lines in frmSlide_Load
Add InitializeSliderButtons method
Add InitializeSliderButtons() line in frmSlide
Handle distanceX == 50 && distanceY == 0 related checks\logic in allNumberButton_Click. The are probably not correct values as those existing buttons look different in width\height\spacing.
public frmSlide()
{
InitializeComponent();
InitializeSliderButtons();
}
public void InitializeSliderButtons()
{
btnArray = new Button[,]
{
{
btn1,
btn2,
btn3
},
{
btn4,
btn5,
btn6
},
{
btn7,
btn8,
btnBlank
}
};
foreach (var button in btnArray)
{
button.Click += new EventHandler(this.allNumberButton_Click);
}
blkButton = btnArray[rows - 1, cols - 1];
blkButton.Text = " ";
}
private void frmSlide_Load(object sender, EventArgs e)
{
// blkButton.Text = " ";
// int curNum = 1;
// for (int x = 0; x < rows; x++)
// {
// for (int y = 0; y < cols; y++)
// {
// Button b;
// b = new Button();
// b.Location = new Point(100 + (y * 50), 100 + (x * 50));
// b.Height = 40;
// b.Width = 40;
// b.Text = curNum.ToString();
// btnArray[x, y] = b;
// b.TextAlign = ContentAlignment.MiddleCenter;
// b.Font = new Font(FontFamily.GenericSerif, (float)15.0);
// b.Click += new System.EventHandler(this.allNumberButton_Click);
// this.Controls.Add(b);
// curNum++;
// }
// }
// btnArray[rows - 1, cols - 1].Text = "";
// blkButton = btnArray[rows - 1, cols - 1];
// blkButton.Text = " ";
}
To determine your form button values, you'll look at the Location property of the Button on the designer (in the Properties window\view)
In the above screenshot, it's 170, 122 (X, Y). The button to the right of it would have to be 220, 122 (X+50, Y+0) for your current logic\check to work. You'll reflect on those locations and differences and update your logic\checks to whatever you determine it should be.
I must say you need to take break and sit back. You are making this FAR more complicated than it has to be. Remember… KISS (Keep It Super Simple).
From what you stated, you are given a form with nine (9) Button objects on the form (btn1, btn2…btn9). The buttons are displayed in a 3x3 array or 3 rows of 3 columns. Given this, it is not a bad idea as you started to make a 3x3 Button array to “hold” the buttons.
One thing that you need to “forget about” at this time is the “blank” button. To start, let’s just say it is another button, it is irrelevant at this point if it is “blank” or not. So, let us fill the 3x3 Button array using the “existing” Buttons on the form. In addition, since we want ALL the button’s Click event to fire the same event, we can go ahead and wire them up also. Something like…
public void InitializeSliderButtons() {
btnArray[0, 0] = btn1;
btnArray[0, 1] = btn2;
btnArray[0, 2] = btn3;
btnArray[1, 0] = btn4;
btnArray[1, 1] = btn5;
btnArray[1, 2] = btn6;
btnArray[2, 0] = btn7;
btnArray[2, 1] = btn8;
btnArray[2, 2] = btn9;
foreach (var button in btnArray) {
button.Click += new EventHandler(allNumberButton_Click);
}
}
Now we have the Buttons in a nice and neat 3x3 array. Next, we need to move to the buttons “Click” event allNumberButton_Click. When this event is fired, the first problem we have is figuring out “which” button was clicked. This is easily done, however, there is one button that we want to “ignore” if it is clicked and it doesn’t matter “which” button it is. That button would be the Button that has the text set to a blank string. In other words, if the blank button is clicked then we do not have to do anything. So, the first check in this event is to check for this blank button.
private void allNumberButton_Click(object sender, EventArgs e) {
Button clickedButton = (Button)sender;
if (clickedButton.Text == "")
return;
// if code execution gets here then the clicked button is NOT the blank button
}
After this if the code did not return, then we DO need to find out “which” Button was clicked. In this case the only way we can tell which button is clicked is by looking at the button name. We already know the button names are btn1, btn2… etc. However, EVEN if we know the button name, then we would still have to figure out “where” that button was in relation to the 3x3 button array. It is irrelevant what the button name is, we need the position in the button array. In other words, I only care about “which” button was clicked in relation to the 3x3 button array.
If we can get the clicked button's X,Y position in the btnArray, then it will make it trivial to check the buttons above, below, left and right of this button. If any of the above, below, left or right buttons are empty/blank, then we can switch the two button’s text value.
Given this, I will use a Point object as it has an X and Y property we can use. To help we will create a very simple method that takes a Button, and returns a Point. We will know the “position” of the button by its name. So btn1 would return Point(0,0), btn2 would return Pont(0,1)… etc. This will help in the click event by giving us the X and Y coordinates of the clicked button in relation to the 3x3 button array. If we are given a button that does not match any of the name’s we are looking for then we will return a -1, -1. This method may look something like…
private Point GetButtonPosition(Button button) {
if (button.Name == "btn1")
return new Point(0, 0);
if (button.Name == "btn2")
return new Point(0, 1);
if (button.Name == "btn3")
return new Point(0, 2);
if (button.Name == "btn4")
return new Point(1, 0);
if (button.Name == "btn5")
return new Point(1, 1);
if (button.Name == "btn6")
return new Point(1, 2);
if (button.Name == "btn7")
return new Point(2, 0);
if (button.Name == "btn8")
return new Point(2, 1);
if (button.Name == "btn9")
return new Point(2, 2);
return new Point(-1, -1);
}
This will update the click method like below…
private void allNumberButton_Click(object sender, EventArgs e) {
Button clickedButton = (Button)sender;
if (clickedButton.Text == "")
return;
Point buttonPosition = GetButtonPosition(clickedButton);
}
Now that we have the clicked buttons position in relation to the 3x3 all we need to do is check to see if one of the buttons above, below, left or right of this button is an “empty/blank” button. In this example, I have created another method that takes a Button and returns a Point object.
In this method, we will check buttons above, below, left and right of the given button and see if any of those buttons are the empty/blank button. If we find one of the buttons IS the empty/blank button, then, we will return the X, Y position in relation to the 3x3 array of that blank button. If none of the buttons above, below, left and right are the blank button, then we will return a -1, -1 to indicate that none of the button are the blank button. This method may look something like…
private Point GetBlankButtonNextToGivenButtonPosition(Point btnPosition) {
// check buttons to the left and right of the given button
if (btnPosition.X - 1 >= 0) {
if (btnArray[btnPosition.X - 1, btnPosition.Y].Text == "") {
return new Point(btnPosition.X - 1, btnPosition.Y);
}
}
if (btnPosition.X + 1 < 3) {
if (btnArray[btnPosition.X + 1, btnPosition.Y].Text == "") {
return new Point(btnPosition.X + 1, btnPosition.Y);
}
}
// Check for buttons above and below the given button
if (btnPosition.Y - 1 >= 0) {
if (btnArray[btnPosition.X, btnPosition.Y - 1].Text == "") {
return new Point(btnPosition.X, btnPosition.Y - 1);
}
}
if (btnPosition.Y + 1 < 3) {
if (btnArray[btnPosition.X, btnPosition.Y + 1].Text == "") {
return new Point(btnPosition.X, btnPosition.Y + 1);
}
}
// blank not found
return new Point(-1, -1);
}
With this, now we can check and see if any of the buttons above, below, right or left of the clicked button is the blank button. When we call the method above, IF we get a Point with values of -1, -1, then we know that we do NOT need to swap the button text values. On the other hand, if we get any other number then that would indicate that there IS a blank button next to the clicked button. With this we would have all the info we need to swap the text values. We have the position of the clicked button, and we also have the position of the blank button “next” to the clicked button. This should complete the Click event code…
private void allNumberButton_Click(object sender, EventArgs e) {
Button clickedButton = (Button)sender;
if (clickedButton.Text == "")
return;
Point buttonPosition = GetButtonPosition(clickedButton);
if (buttonPosition.X != -1) {
Point blankPosition = GetBlankButtonNextToGivenButtonPosition(buttonPosition);
if (blankPosition.X != -1) {
btnArray[blankPosition.X, blankPosition.Y].Text = clickedButton.Text;
clickedButton.Text = "";
}
}
if (BoardIsCorrect()) {
MessageBox.Show("You solved the puzzle!");
}
}
I have another method called BoardIsCorrect to check and see if the numbers are in the correct sequence and if so, return true. This and the start and reset methods are also added below.
public Button[,] btnArray = new Button[3, 3];
Random rand = new Random();
private void frmSlide_Load(object sender, EventArgs e) {
InitializeSliderButtons();
}
private bool BoardIsCorrect() {
if (btnArray[0, 0].Text == "1" &&
btnArray[0, 1].Text == "2" &&
btnArray[0, 2].Text == "3" &&
btnArray[1, 0].Text == "4" &&
btnArray[1, 1].Text == "5" &&
btnArray[1, 2].Text == "6" &&
btnArray[2, 0].Text == "7" &&
btnArray[2, 1].Text == "8"
) {
return true;
}
return false;
}
private void btnStart_Click(object sender, EventArgs e) {
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int nextRand;
int randIndex;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
randIndex = rand.Next(0, numbers.Count);
nextRand = numbers[randIndex];
if (nextRand != 9) {
btnArray[i, j].Text = nextRand.ToString();
}
else {
btnArray[i, j].Text = "";
}
numbers.RemoveAt(randIndex);
}
}
}
private void btnReset_Click(object sender, EventArgs e) {
int rows = 3;
int cols = 3;
int count = 1;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
btnArray[r, c].Text = count++.ToString();
}
}
btnArray[2, 2].Text = "";
}
I hope this make sense.
I want to be able to declare multiple separate pictureboxes with different positions, backgroundcolors and tags. I would like to use the tags to refer to the pictureboxes in my code, to change and/or remove them individually.
How do I go about this?
I have attempted to create a separate class, meant for creating the same picturebox as a public static but encountered some issues with the trbRed.Value values, since they don't exist in that class. I am not sure if this solution would work for my cause, and neither do I have a clue on how to create the class either way.
This program uses drag and drop, and therefore uses the mousepositions
to create pictureboxes with different backcolors.
picBlock is declared earlier in the class with
PictureBox picBlock = new PictureBox();.
private void myPanel_MouseMove(object sender, MouseEventArgs e)
{
int mouseX = e.X / 25 + 1;
int mouseY = (myPanel.Size.Height - e.Y) / 25 + 1;
if (myPanel.BackgroundImage != null)
{
lblMousePos.Show();
lblMousePos.Text = "pos (" + mouseX + ", " + mouseY + ")";
}
}
public void myPanel_DragDrop(object sender, DragEventArgs e)
{
picBlock = new PictureBox
{
BackColor = Color.FromArgb(255, trbRed.Value,
trbGreen.Value, trbBlue.Value),
Image = Image.FromFile("my picture here.png"),
Size = new Size(25, 24),
Location = new Point((mouseX - 1) * 25,
this.myPanel.Height - mouseY * 24),
SizeMode = PictureBoxSizeMode.Zoom,
Tag = mouseX.ToString() + ", " + mouseY.ToString()
};
listX_pos.Add(mouseX);
listY_pos.Add(mouseY);
picBlock.MouseMove += picBlock_mousemove;
picBlock.MouseDown += picBlock_mousedown;
this.myPanel.Controls.Add(picBlock);
}
public void picBlock_mousedown (object sender, MouseEventArgs e)
{
this.myPanel.Controls.Remove(picBlock);
for (int i = 0; i < listX_pos.Count; i++)
{
if (listX_pos[i] == ((picBlock.Location.X / 25) + 1) && listY_pos[i] == (this.myPanel.Location.Y - picBlock.Location.Y / 25) - 11)
{
listX_pos.RemoveAt(i);
listY_pos.RemoveAt(i);
}
}
//pcbPicture is a picturebox with the same content of "my picture here.png"
picBlock.DoDragDrop(pcbPicture.Image, DragDropEffects.Move);
}
public void picBlock_mousemove(object sender, MouseEventArgs e)
{
if (myPanel.BackgroundImage != null)
{
//a label to show the position of the mouse
lblMousePos.Show();
Point point = myPanel.PointToClient(MousePosition);
int mouseX = point.X / 25 + 1;
int mouseY = (myPanel.Size.Height - point.Y) / 25 + 1;
lblMousePos.Text = "pos (" + mouseX + ", " + mouseY + ")";
}
}
I have no issues creating the pictureboxes, but can only change the position of the most recently created picturebox from picBlock_mousemove as well as picBlock_mousedown voids.
I want to be able to change the position for any of the created pictureboxes as well as their own controls (mousemove and mousedown).
As #rene said, your problem is that in your picBlock mouse handlers, you refer to picBlock, which contains only the last image created.
The solution is in the event. Each event in .NET, has two properties, by convention: object sender and System.EventArgs e.
e is the more understable; It's a class drived from System.EventArgs (or itself) that contains some required information about the event: the pressed mouse button/keyboard key, mouse X and Y, and so on - any relevant information.
sender is less usable. It contains the object that triggered the event.
Why? Can't we access it directly, such as by member variable?
The answer is your (and similar) cases: we attach a single method to events of some objects, then we want to know on which element to work. For example, a simple example that pops up a messagebox every time you click a button, with it's content:
private void Form_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var btn = new Button
{
Text = "Button #" + i, Top = i * 50, Left = 0
};
btn.Click += btn_Click;
this.Controls.Add(btn);
}
}
private void btn_Clicked(object sender, EventArgs e)
{
MessageBox.Show(((Button)sender).Text); // Access the clicked button by `sender`
}
Returning to your question, use it like (you don't need a member variable picBlock):
private void myPanel_MouseMove(object sender, MouseEventArgs e)
{
if (myPanel.BackgroundImage != null)
{
lblMousePos.Show();
lblMousePos.Text = "pos (" + (e.X / 25 + 1) + ", " + ((myPanel.Size.Height - e.Y) / 25 + 1) + ")";
}
}
public void myPanel_DragDrop(object sender, DragEventArgs e)
{
var picBlock = new PictureBox
{
BackColor = Color.FromArgb(255, trbRed.Value,
trbGreen.Value, trbBlue.Value),
Image = Image.FromFile("my picture here.png"),
Size = new Size(25, 24),
Location = new Point((mouseX - 1) * 25,
this.myPanel.Height - mouseY * 24),
SizeMode = PictureBoxSizeMode.Zoom,
//Tag = musX.ToString() + ", " + musY.ToString()
// You don't need the Tag property
};
picBlock.MouseMove += picBlock_mousemove;
picBlock.MouseDown += picBlock_mousedown;
this.myPanel.Controls.Add(picBlock);
}
public void picBlock_mousedown (object sender, MouseEventArgs e)
{
var picBlock = (PictureBlock)sender;
this.panelSpel.Controls.Remove(picBlock);
for (int i = 0; i < listX_pos.Count; i++)
{
if (listX_pos[i] == ((picBlock.Location.X / 25) + 1) && listY_pos[i] == (this.myPanel.Location.Y - picBlock.Location.Y / 25) - 11)
{
listX_pos.RemoveAt(i);
listY_pos.RemoveAt(i);
}
}
//pcbPicture is a picturebox with the same content of "my picture here.png"
picBlock.DoDragDrop(pcbPicture.Image, DragDropEffects.Move);
}
public void picBlock_mousemove(object sender, MouseEventArgs e)
{
var picBlock = (PictureBlock)sender;
if (myPanel.BackgroundImage != null)
{
//a label to show the position of the mouse
lblMousePos.Show();
Point point = myPanel.PointToClient(MousePosition);
int mouseX = point.X / 25 + 1;
int mouseY = (myPanel.Size.Height - point.Y) / 25 + 1;
lblMousePos.Text = "pos (" + musX + ", " + musY + ")";
}
}
I've been trying to create connect 4 (the game) in windows forms applications in C# but I'm kinda struggling to create a method so the coins actually drop and not stay were the user clicks. Also I don't know where should I place the win condition method. Here is my code so far:
namespace ConnectFour
{
public partial class Form1 : Form
{
Button[] gameButtons = new Button[42]; //array of buttons for markers(red and blue)
bool blue = true; //blue is set to true if the next marker is to be a blue
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "Connect 4";
this.BackColor = Color.BlanchedAlmond;
this.Width = 500;
this.Height = 500;
for (int i = 0; i < gameButtons.Length; i++)
{
int index = i;
this.gameButtons[i] = new Button();
int x = 50 + (i % 7) * 50;
int y = 50 + (i / 7) * 50;
this.gameButtons[i].Location = new System.Drawing.Point(x, y);
this.gameButtons[i].Name = "btn" + (index + 1);
this.gameButtons[i].Size = new System.Drawing.Size(50, 50);
this.gameButtons[i].TabIndex = i;
//this.gameButtons[i].Text = Convert.ToString(index);
this.gameButtons[i].UseVisualStyleBackColor = true;
this.gameButtons[i].Visible = true;
gameButtons[i].Click += (sender1, ex) => this.buttonHasBeenPressed(sender1, index);
this.Controls.Add(gameButtons[i]);
}
}
private void buttonHasBeenPressed(object sender, int i)
{
if (((Button)sender).BackColor == Color.BlanchedAlmond)
{
if (blue == true)
{
((Button)sender).BackColor = Color.Red;
}
else
{
((Button)sender).BackColor = Color.Blue;
}
blue = !blue;
}
}
private void fourInARow(int a, int b, int c,int d)
{
if (gameButtons[a].BackColor == gameButtons[b].BackColor && gameButtons[a].BackColor == gameButtons[c].BackColor && gameButtons[a].BackColor==gameButtons[d].BackColor)
{
if (gameButtons[a].BackColor == Color.Blue)
{
MessageBox.Show("the winner is player 1");
}
else
{
MessageBox.Show("the winner is player 2");
}
}
}
}
}
You've probably figured it out by now, but what I would do is take a look at the BackColor of each Button below the one the user pressed until we either hit the bottom row or we find a Button that doesn't have a "BlanchedAlmond" BackColor, and then that's the one we change.
To find a Button directly below another one, we just look at the Button with an Index of 7 more than the current Index. And since we pass the Button object's index in the index argument to our function, we can use that.
Here's a commented code example:
private void ButtonHasBeenPressed(object sender, int index)
{
// Get the button that the user pressed
var pressedButton = (Button)sender;
// Only do something if they clicked a "neutral" button
if (pressedButton.BackColor == Color.BlanchedAlmond)
{
// The backcolor will be set based on whether or not blue is true or false
var newBackColor = blue ? Color.Red : Color.Blue;
// Get the index of the button that the user clicked
var buttonToChangeIndex = index;
// From where the user clicked, look down at each button below (index + 7)
// until we find the last button that has a BlanchedAlmond backcolor
while (buttonToChangeIndex + 7 < gameButtons.Count() &&
gameButtons[buttonToChangeIndex + 7].BackColor == Color.BlanchedAlmond)
{
buttonToChangeIndex += 7; // Set to the index to point to this button
}
// Now we set that button's backcolor
gameButtons[buttonToChangeIndex].BackColor = newBackColor;
// Flip our blue flag
blue = !blue;
}
}
The fact that you use a one-dimensional array to hold the buttons makes things a little more difficult. Since the layout of this “connect four” game is two-dimensional, it would seem logical to make a two-dimensional array. This will help facilitate the “Drop” in a column. Your current code will need to translate which cells in the one-dimensional array make up a column, namely the one the user clicked.
The code below solves this “Dropping” issue by using a two-dimensional array with a method that gets the ROW index of the next available space in a given column. This method returns the next available open slot index in the given column starting at the bottom and going up or returns -1 if the column is full.
You should seriously consider making a class of tokens/markers/buttons, then another Class that is a game board for these tokens. The logic to see if there are 4 consecutive colors horizontally, vertically and diagonally could possibly be complex. However, this logic would fit perfectly towards a GameBoard Object. In addition, the “GameBoard” object could keep track of all the moves etc…
This will greatly free up your code to focus on the visual aspects of the form and not worry about the logic of the game. I hope this makes sense.
Button[,] gameButtons = new Button[7,6]; //array of buttons for markers(red and blue)
bool blue = true; //blue is set to true if the next marker is to be a blue
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
this.Text = "Connect 4";
this.BackColor = Color.BlanchedAlmond;
this.Width = 500;
this.Height = 500;
int x;
int y;
for (int row = 0; row < gameButtons.GetLength(0); row++) {
x = 50 + (row % 7) * 50;
for (int col = 0; col < gameButtons.GetLength(1); col++) {
y = 50*col + 50;
Button newButton = new Button();
newButton.Location = new System.Drawing.Point(x, y);
newButton.Name = "btn" + (row + col + 1);
newButton.Size = new System.Drawing.Size(50, 50);
newButton.TabIndex = row + col;
newButton.UseVisualStyleBackColor = true;
newButton.Visible = true;
newButton.Click += (sender1, ex) => this.buttonHasBeenPressed(sender1);
gameButtons[row, col] = newButton;
Controls.Add(gameButtons[row,col]);
}
}
}
private void buttonHasBeenPressed(object sender) {
Button buttonClicked = (Button)sender;
int col = buttonClicked.Location.X / 50 - 1;
int targetRow = GetButtonRow(col);
if (targetRow >= 0) {
if (blue == true) {
gameButtons[col, targetRow].BackColor = Color.Red;
} else {
gameButtons[col, targetRow].BackColor = Color.Blue;
}
blue = !blue;
}
}
public int GetButtonRow(int colIndex) {
Button curButton;
for (int row = gameButtons.GetLength(1) - 1; row >= 0; row--) {
curButton = gameButtons[arrayColIndex, row];
if (curButton.BackColor != Color.Red && curButton.BackColor != Color.Blue) {
return row;
}
}
return -1;
}
I have something like chessboard, 21x21 checks, each of one have 10x10pixels. I have no idea how do I paint one of the check when user click mouse on it.
Probably i have to use arrays but i dont know how can I assign 10x10 pixels to one element of other array. Can i assign array wiht 100 elements to one elemnet of other array?
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace gotowa_mapa
{
public partial class Form1 : Form
{
Bitmap mapa;
private int szer_pb = 200;
private int wys_pb = 200;
private int ilosc_kratek_x = 41;
private int ilosc_kratek_y = 41;
// private void wpisanie_tablic()
//{
// int[,] tablica_kratki = new int[ilosc_kratek_x, ilosc_kratek_y];
// int[,] tablica_piksele = new int[szer_pb, wys_pb];
//for(int i=0;i<ilosc_kratek_x; i++)
// {
// for(int j=0; j<ilosc_kratek_y; j++)
//{
// tablica_kratki[ilosc_kratek_x, ilosc_kratek_y] = tablica_piksele[2,2];
//}
//}
//}
private void siatka1()
{
mapa = new Bitmap(szer_pb, wys_pb);
int factor_x = (int)(szer_pb / ilosc_kratek_x);
int factor_y = (int)(wys_pb / ilosc_kratek_y);
for (int i = 0; i < szer_pb; i++)
{
if (i > (factor_x * ilosc_kratek_x))
break;
for (int j = 0; j < wys_pb; j++)
{
if (j > (factor_y * ilosc_kratek_y))
break;
if (i % (factor_x) == 0 || j % (factor_y) == 0)
{
mapa.SetPixel(i, j, Color.Black);
mapa.SetPixel(j, i, Color.Black);
}
else
{
mapa.SetPixel(i, j, Color.White);
mapa.SetPixel(j, i, Color.White);
}
}
}
}
// private void rysowanie_scian(int x, int y)
// {
// }
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
siatka1();
pictureBox1.Image = mapa;
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
int x, y;
x = e.X;
y = e.Y;
MessageBox.Show(x + " " + y);
}
}
}
I've made an example for you, although there is probably better ways of doing this, using some simple math you can achieve this.
A neat trick is overriding the onPaint method of a panel, making a new Bitmap with the same size as the panel, drawing this image over the panel during the onPaint method, making changes directly to the image, then invalidating the panel so it draws the image. (You can then save the image with Bitmap.Save)
Keep in mind, if a game of some sort is what you are aiming for, GDI is not ideal, look into managed DirectX or similar.
//declare variables
public const int offset = 50;
public const int boardX = 10;
public const int boardY = 10;
public const int checkSize = 40;
int[] checks;
//set initial values for the checks array
if(checks == null)
{
checks = new int[boardX * boardY];
}
//start iterating through the array of checks
for (int i = 0; i < checks.Length; i++)
{
//if the check is empty (assuming you will use integers as identifiers for the pieces on the board)
if (checks[i] < 1)
{
//check if the check in array is odd number & paint odd numbers black on alternating rows
if (i % 2 != 0)
{
//check if row is odd
if (Math.Ceiling((double)(i + 1) / boardX) % 2 != 0)
{
drawCheck(Brushes.Black, e.Graphics, i);
}
else
{
drawCheck(Brushes.White, e.Graphics, i)
}
}
else
{
//check if row is odd
if (Math.Ceiling((double)(i+1) / boardX) % 2 != 0;
{
drawCheck(Brushes.White, e.Graphics, i);
}
else
{
drawCheck(Brushes.Black, e.Graphics, i)
}
}
}
else if (checks[i] == 1)
{
//the check is a "clicked check" - draw it red
drawCheck(Brushes.Red, e.Graphics, i);
}
}
//The Methods
private void YOURDRAWINGSURFACE_MouseClick(object sender, MouseEventArgs e)
{
if(e.X < (boardX * checkSize) + offset && e.Y < (boardY * checkSize) + offset && e.X > offset && e.Y > offset )
{
//you have clicked somewhere on the board. if that square isn't red, make it so, else turn it back to a normal square
if (checks[checkClicked(e.X, e.Y)] != 1)
{
checks[checkClicked(e.X, e.Y)] = 1;
}
else
{
checks[checkClicked(e.X, e.Y)] = 0;
}
}
//this is only relevant for GDI drawing code. It will invalidate the form to redraw the surface (needs to be called whenever you make a change)
this.Invalidate();
}
public int checkClicked(int mouseX, int mouseY)
{
//this method returns the int of the particular check that was clicked in the array.
return ((int)Math.Ceiling((double)(mouseX - offset) / checkSize) + (int)((Math.Ceiling((double)(mouseY - offset) / checkSize)) * boardX) - (1 + boardX));
}
public void drawCheck(Brush brush, Graphics graphics, int checkNumber)
{
//this method draws the check given the parameters.
graphics.FillRectangle(brush, ((checkNumber % boardX) * checkSize) + offset, (int)(Math.Ceiling((double)(checkNumber-(boardX-1)) / boardX) * checkSize) + offset, checkSize, checkSize);
}
I have a Multiline richtextbox control into which i want to integrate the feature of adding a line number. i have considered many approaches
Add a label and updating the line numbers as the line count changes
Add a picturebox along with to draw string on it.
Add another textbox along with and show line numbers on it
Add listbox along and display line numbers in it.
I got two doubts.
The richtextbox which i'm using is a custom made control and derieves from RichTextBox class. How can i add multiple controls to it.
What is the best approach to show line numbers for the multiline text in c#
My own example. All is fine, but wordwrap must be disabled :(
int maxLC = 1; //maxLineCount - should be public
private void rTB_KeyUp(object sender, KeyEventArgs e)
{
int linecount = rTB.GetLineFromCharIndex( rTB.TextLength ) + 1;
if (linecount != maxLC)
{
tB_line.Clear();
for (int i = 1; i < linecount+1; i++)
{
tB_line.AppendText(Convert.ToString(i) + "\n");
}
maxLC = linecount;
}
}
where rTB is my richtextbox and tB is textBox next to rTB
J.T. jr
this code helped me thank you, needed to convert visual basic but could:
Private Sub TextBox1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyUp
Dim maxlc As Integer = 1
Dim linecount As Integer = TextBox1.GetLineFromCharIndex(TextBox1.Height) + 1
If linecount <> maxlc Then
TextBox2.Clear()
For i = 0 To linecount - 1 Step 1
TextBox2.AppendText(Convert.ToString(i) + vbNewLine)
Next i
maxlc = linecount
End If
End Sub
public int getWidth()
{
int w = 25;
// get total lines of richTextBox1
int line = richTextBox1.Lines.Length;
if (line <= 99)
{
w = 20 + (int)richTextBox1.Font.Size;
}
else if (line <= 999)
{
w = 30 + (int)richTextBox1.Font.Size;
}
else
{
w = 50 + (int)richTextBox1.Font.Size;
}
return w;
}
public void AddLineNumbers()
{
// create & set Point pt to (0,0)
Point pt = new Point(0, 0);
// get First Index & First Line from richTextBox1
int First_Index = richTextBox1.GetCharIndexFromPosition(pt);
int First_Line = richTextBox1.GetLineFromCharIndex(First_Index);
// set X & Y coordinates of Point pt to ClientRectangle Width & Height respectively
pt.X = ClientRectangle.Width;
pt.Y = ClientRectangle.Height;
// get Last Index & Last Line from richTextBox1
int Last_Index = richTextBox1.GetCharIndexFromPosition(pt);
int Last_Line = richTextBox1.GetLineFromCharIndex(Last_Index);
// set Center alignment to LineNumberTextBox
LineNumberTextBox.SelectionAlignment = HorizontalAlignment.Center;
// set LineNumberTextBox text to null & width to getWidth() function value
LineNumberTextBox.Text = "";
LineNumberTextBox.Width = getWidth();
// now add each line number to LineNumberTextBox upto last line
for (int i = First_Line; i <= Last_Line + 2; i++)
{
LineNumberTextBox.Text += i + 1 + "\n";
}
}
private void Form1_Load(object sender, EventArgs e)
{
LineNumberTextBox.Font = richTextBox1.Font;
richTextBox1.Select();
AddLineNumbers();
}
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
Point pt = richTextBox1.GetPositionFromCharIndex(richTextBox1.SelectionStart);
if (pt.X == 1)
{
AddLineNumbers();
}
}
private void richTextBox1_VScroll(object sender, EventArgs e)
{
LineNumberTextBox.Text = "";
AddLineNumbers();
LineNumberTextBox.Invalidate();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
if (richTextBox1.Text == "")
{
AddLineNumbers();
}
}
private void richTextBox1_FontChanged(object sender, EventArgs e)
{
LineNumberTextBox.Font = richTextBox1.Font;
richTextBox1.Select();
AddLineNumbers();
}
private void LineNumberTextBox_MouseDown(object sender, MouseEventArgs e)
{
richTextBox1.Select();
LineNumberTextBox.DeselectAll();
}
private void Form1_Resize(object sender, EventArgs e)
{
AddLineNumbers();
}
WORKS 100%!!! But you need to add richTextBox2 for line numbers, if you want change it to other
form like listbox, anyway it served me well.
private void richTextBox1_keyDown(object sender, KeyEventArgs e)
{
for (int i = 0; i <= richTextBox1.Lines.Count(); i++)
{
if (!(e.KeyCode == Keys.Back))
{
if (!richTextBox2.Text.Contains(i.ToString()))
{
richTextBox2.Text += i.ToString() + "\n";
}
}
else
{
richTextBox2.Clear();
}
}
}