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'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;
}
This function dynamically creates nine buttons for use in a game I am making. You can see what attributes I give to the button.
private void createbuttons()
{
int tot = 0;
int x = 100;
int y = 100;
while(tot < 9)
{
string buttonsname = (tot + "button").ToString();
Button creating = new Button();
creating.Name = buttonsname;
creating.Size = new Size(100, 100);
creating.Click += delegate
{
MessageBox.Show("You clicked me!");
};
creating.Text = buttonsname;
if(x > 300)
{
y += 100;
x = 100;
}
creating.Location = new Point(x, y);
Controls.Add(creating);
tot += 1;
x += 100;
}
}
What I want to know is how to reference these buttons in different parts of the same form. Specifically when 'Start Game' is clicked I want to change the text for each button to something different.
private void button10_Click(object sender, EventArgs e)
{
//What would I write here to change the text?
}
You can access the buttons by enumerating the controls, or you could create a list of buttons for future reference, and use that list later.
Here is how you do it with a list:
private IList<Button> addedButtons = new List<Button>();
private void createbuttons() {
int tot = 0;
int x = 100;
int y = 100;
while(tot < 9) {
string buttonsname = (tot + "button").ToString();
Button creating = new Button();
creating.Name = buttonsname;
creating.Size = new Size(100, 100);
creating.Click += delegate {
MessageBox.Show("You clicked me!");
};
creating.Text = buttonsname;
if(x > 300) {
y += 100;
x = 100;
}
creating.Location = new Point(x, y);
addedButtons.Add(creating); // Save the button for future reference
Controls.Add(creating);
tot += 1;
x += 100;
}
}
Now you can do this:
foreach (var btn : addedButtons) {
btn.Text = "Changed "+btn.Text;
}
The form has a property Controls that holds all child controls. To find a child control by its Name property use method Find, which returns the array, because there may be several control with the same Name, but if you make sure that names exist, are unique, and you know their type (Button) you can just take the first item from the array and cast it:
private void button10_Click(object sender, EventArgs e)
{
Button buttonNamedFred = (Button)this.Controls.Find("Fred", false)[0];
buttonNamedFred.Text = "I'm Fred";
}
I'm experimenting with a form that contains some vertically stacked panels and I'm trying to implement some drag and drop so they can be re-ordered. It's working well so far apart from one bug that I can't figure out.
The premise is that it's a list of files which must be kept vertically stacked, but can be re-ordered as the user sees fit, to form a list of pages for a document I'll produce later.
Try the following code and you should get a form with 6 stacked panels. Click and drag any one of them up or down and they will re-order.
The problem is that if you drag one up and down really fast they start to overlap with each other.
I've put the shortcut CTRL + B in there to list the current panel top positions at any given time and you'll see that you get duplicate numbers when you shouldn't.
The problem is with the MouseMove event. I'm sure it's something like a race condition where the dictionary can't keep up or the indexes don't calculate fast enough but I'm scratching my head. I've tried locking them but it hasn't helped.
I'm sure there's a better way to implement this so I'm open to suggestions, but given that this will never have more than say 30 panels this suits my needs.
Apologies for the messy-ness, I was planning to tidy up later!
public Form1()
{
InitializeComponent();
this.SuspendLayout();
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 601);
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(300, 640);
this.MinimumSize = new System.Drawing.Size(300, 640);
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.ResumeLayout(false);
}
Dictionary<int, Panel> panelPositions = new Dictionary<int, Panel>();
static Random rnd = new Random();
int y;
int start;
static int index, previndex, currentindex, newindex, maxindex;
bool isDragging;
Panel currentPanel;
static readonly object Lock = new Object();
private void Form1_Load(object sender, EventArgs e)
{
Width = 300;
Height = 640;
int count = 5;
int currentTop = 0;
for (int i = 0; i <= count; i++)
{
Panel panel = new Panel();
panel.Width = 300;
panel.Height = 100;
panel.Top = currentTop;
panel.BackColor = RandomColor();
panel.Margin = Padding.Empty;
panel.Padding = Padding.Empty;
panel.BorderStyle = BorderStyle.None;
Label label = new Label();
label.Font = new Font("Segoe UI", 24.0f);
label.Text = (i + 1).ToString();
label.Top = 20;
label.Left = 20;
label.AutoSize = true;
panel.Controls.Add(label);
panel.MouseDown += new MouseEventHandler(MouseisDown);
panel.MouseMove += new MouseEventHandler(MouseMoves);
panel.MouseUp += new MouseEventHandler(MouseisUp);
lock (Lock)
{
panelPositions.Add(i, panel);
}
Controls.Add(panel);
currentTop += 100;
}
lock (Lock)
{
maxindex = panelPositions.Count - 1;
}
}
private void MouseisUp(object sender, MouseEventArgs e)
{
if (isDragging)
{
if (newindex < maxindex)
{
currentPanel.Top = newindex * 100;
}
else
{
currentPanel.Top = maxindex * 100;
}
}
isDragging = false;
}
// I'M SURE THE PROBLEM IS IN HERE SOMEWHERE.
private void MouseMoves(object sender, MouseEventArgs e)
{
// CHECK THE MOUSE IS STILL DOWN
if (isDragging)
{
// DRAG PANEL VERTICALLY WITH MOUSE
currentPanel.Location = new Point(currentPanel.Left, e.Y + currentPanel.Top - y);
// WORK OUT NEW INDEX POSITION
newindex = 0;
if ((currentPanel.Top + e.Y) > 0)
{
newindex = ((currentPanel.Top + e.Y) / 100);
}
// NEW POSITION?
if (currentindex != newindex)
{
// TRACK CHANGES
previndex = currentindex;
currentindex = newindex;
/* PRETTY SURE IT'S THIS BIT THAT'S WRONG */
// CHECK WE'RE NOT OUT OF BOUNDS
if (currentindex <= maxindex)
{
lock (Lock)
{
// RE-ARRANGE PANEL INDEX
panelPositions[previndex] = panelPositions[currentindex];
panelPositions[currentindex] = currentPanel;
panelPositions[previndex].Top = previndex * 100;
}
}
}
}
}
private void MouseisDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
currentPanel = sender as Panel;
isDragging = true;
currentPanel.BringToFront();
y = e.Y;
start = currentPanel.Top;
int i = 0;
if (currentPanel.Top > 0)
{
i = currentPanel.Top;
i = (i / 100);
}
index = i;
previndex = i;
currentindex = i;
}
else
{
isDragging = false;
}
}
private Color RandomColor()
{
Color randColor;
randColor = Color.FromArgb(RandomRGB(), RandomRGB(), RandomRGB());
return randColor;
}
private int RandomRGB()
{
return rnd.Next(1, 256);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.B)
{
string message = string.Empty;
int count = panelPositions.Count;
for (int i = 0; i < count; i++)
{
message += #"Panel " + i.ToString() + #": " + panelPositions[i].Top.ToString() + Environment.NewLine;
}
MessageBox.Show(message);
}
}
Thanks!
Edit: I think it's something to do with when the panel goes outside the bottom bounds of the form. If you drag within the form the issue doesn't occur. I think it's to do with the previous index, but I can't see it.
Change
// NEW POSITION?
if (currentindex != newindex)
To
// NEW POSITION?
if (currentindex != newindex && newindex <= maxindex)
I think you know why :)
P.s: Your "Edit" section is a bit misleading.
I have an array of buttons, like this:
int x = 0, y = 0;
butt2 = new Button[100];
for (int i = 0; i < 100; i++)
{
butt2[i] = new Button();
int names = i;
butt2[i].Name = "b2" + names.ToString();
butt2[i].Location = new Point(525 + (x * 31), 70 + (y * 21));
butt2[i].Visible = true;
butt2[i].Size = new Size(30, 20);
butt2[i].Click += new EventHandler(butt2_2_Click); //problem lies here (1)
this.Controls.Add(butt2[i]);
}
private void butt2_2_Click(object sender, EventArgs e)
{
// want code here
}
I want to change the back color of the button when clicked. I was thinking of passing i to be able to do this:
butt2[i].BackColor = Color.Green;
This should do the trick:
private void butt2_2_Click(object sender, EventArgs e)
{
Button pushedBtn = sender as Button;
if(pushedBtn != null)
{
pushedBtn.BackColor = Color.Green;
}
}
And this holds for most UI events, the 'object sender' parameter refers to the control that 'sent'/'fired' the event.
To learn more about C# event handling, I would start here.
Also, here is a SO question about GUI event handling, answered nicely by Juliet (accepted answer).
Hope this helps.