When I run the following code, it populates the label1 control one time. Then the label1 control does nothing else. How do I get the label1 control to change on the mouse enter events. Please provide code examples.
int currentXposition, currentYposition;
const string positionLabel = "Current Position: ";
private void Test_Load(object sender, EventArgs a)
{
var temp=Color.Transparent; //Used to store the old color name of the panels before mouse events
var colorName = Color.Red; //Color used to highlight panel when mouse over
int numBlocks = 8; //Used to hold the number of blocks per row
int blockSize=70;
//Initialize new array of Panels new
string[,] Position = new string[8, 8];
Panel[,] chessBoardPanels = new Panel[numBlocks, numBlocks];
string Alphabet = "A,B,C,D,E,F,G,H";
string Numbers ="1,2,3,4,5,6,7,8";
string[] alphaStrings = Alphabet.Split(',');
string[] numStrings=Numbers.Split(',');
// b = sub[0];
int FirstValue, SecondValue;
//Store Position Values
for (int firstValue = 0; firstValue < 8; ++firstValue)
{
FirstValue = Alphabet[firstValue];
for (int SecValue = 0; SecValue < 8; ++SecValue)
{
SecondValue = Numbers[SecValue];
Position[firstValue, SecValue] = alphaStrings[firstValue] + numStrings[SecValue];
}
}
//Loop to create panels
for (int iRow = 0; iRow < numBlocks; iRow++)
{
for (int iColumn = 0; iColumn < numBlocks; iColumn++)
{
Panel p = new Panel();
//set size
p.Size = new Size(blockSize, blockSize);
//set back colour
p.BackColor = (iRow + (iColumn % 2)) % 2 == 0 ? Color.Black : Color.White;
//set location
p.Location = new Point(blockSize *iRow+15, blockSize * iColumn+15);
chessBoardPanels[iRow, iColumn] = p;
chessBoardPanels[iRow,iColumn].MouseEnter += (s,e) =>
{
currentXposition = iRow;
currentYposition = iColumn;
var oldColor = (s as Panel).BackColor;
(s as Panel).BackColor = colorName;
temp = oldColor;
label1.Text = Position[iRow, iColumn];
};
chessBoardPanels[iRow, iColumn].MouseLeave += (s, e) => {
(s as Panel).BackColor = temp;
};
groupBox1.Controls.Add(p);
}
}
}
I'm answering this only because I think it's important to show what happens when scope is confused. The reason you're having your issue is the scope of your variables. Your label is changing iRow * iColumn times, but only during initial execution. From then on, iRow and iColumn are fixed at their final values.
To achieve your desired end goal, it'd be easiest to create an extension of Panel:
public class ChessPanel : Panel {
private const Color HighlightColor = Color.Red;
public int iColumn { get; set; }
public int iRow { get; set; }
public Color PrimaryColor { get; set; }
public ChessPanel() : base()
{
this.MouseEnter += (s,e) =>
{
this.PrimaryColor = this.BackColor;
this.BackColor = HighlightColor;
};
this.MouseLeave += (s,e) =>
{
this.BackColor = this.PrimaryColor;
};
}
}
This will then allow you to reduce your code as follows:
int currentXposition, currentYposition;
const string positionLabel = "Current Position: ";
private void Test_Load(object sender, EventArgs a)
{
var temp=Color.Transparent; //Used to store the old color name of the panels before mouse events
var colorName = Color.Red; //Color used to highlight panel when mouse over
int numBlocks = 8; //Used to hold the number of blocks per row
int blockSize=70;
//Initialize new array of Panels new
string[,] Position = new string[8, 8];
ChessPanel[,] chessBoardPanels = new ChessPanel[numBlocks, numBlocks];
string Alphabet = "A,B,C,D,E,F,G,H";
string Numbers ="1,2,3,4,5,6,7,8";
string[] alphaStrings = Alphabet.Split(',');
string[] numStrings=Numbers.Split(',');
int FirstValue, SecondValue;
//Store Position Values --- no idea what this is supposed to do...
for (int firstValue = 0; firstValue < 8; ++firstValue)
{
FirstValue = Alphabet[firstValue];
for (int SecValue = 0; SecValue < 8; ++SecValue)
{
SecondValue = Numbers[SecValue];
Position[firstValue, SecValue] = alphaStrings[firstValue] + numStrings[SecValue];
}
}
//Loop to create panels
for (int iRow = 0; iRow < numBlocks; iRow++)
{
for (int iColumn = 0; iColumn < numBlocks; iColumn++)
{
ChessPanel p = new ChessPanel();
//set size
p.Size = new Size(blockSize, blockSize);
//set back colour
p.BackColor = (iRow + (iColumn % 2)) % 2 == 0 ? Color.Black : Color.White;
//set location
p.Location = new Point(blockSize *iRow+15, blockSize * iColumn+15);
p.MouseEnter += (s,e) =>
{
var cpSelf = s as ChessPanel;
if (cpSelf != null)
{
label1.Text = Position[cpSelf.iRow, cpSelf.iColumn];
}
};
groupBox1.Controls.Add(p);
chessBoardPanels[iRow, iColumn] = p;
}
}
}
I'm assuming you're making use of many of those variables later on int the program, and looking at this kind of makes my head hurt, so I left most of it in place.
Delegates are a very powerful and useful utility, but they can cause confusion with variable scope. Be very careful when using these, and make sure you treat them as functional units that execute later and can therefore only rely on the state of the program at execution of the block rather than at creation of the block. If you notice, I kept the reference to the Position array simply to show that you can still access local variables within the scope of the delegate because it is technically still in scope. Structurally, this could easily be moved into the ChessPanel class and referenced 100% locally. This example should be used as a caution as it can show how a "local" variable that many people assume are garbage-collected at end of function execution can hang around and eat up memory.
This code is untested and may have minor syntax errors. Hopefully the spirit of the structure is understood.
Related
I am working on a Tic Tac Toe simulator for a class and have run into an issue.
I created a 2-dimensional array to simulate the board and populate it with either 0 or 1 in all the boxes.
The issue I am having is getting those numbers to apply to the labels I have created (a1, a2, a3, b1, b2, etcetera).
Is there a way that my nested for loops can have each element in the array apply to a new label? I can't seem to find anything in my book or online about this.
Here is my related code:
private void newBTN_Click(object sender, EventArgs e)
{
Random rand = new Random();
const int ROWS = 3;
const int COLS = 3;
int [,] board = new int[ROWS, COLS];
for (int row = 0; row < ROWS; row++)
{
for (int col = 0; col < COLS; col++)
{
board[row, col] = rand.Next(1);
}
}
}
What are the names of the labels? I assumed below that the labels are Label0_0, Label0_1, Label1_1 and so on... This way you can find them using the row and column values.
You want to find the Label control on your form dynamically, because you don't know the name in advance while coding.
If you know the name in advance you just say: label1.Text = "1";.
But in your case, you are trying to find a particular control in each iteration of the loop. So you need to have a name for the labels so you can find them using Form.Controls.Find(string, bool) like this:
var row = 4;
var col = 6;
var l = this.Controls.Find("Label" + row.ToString() + "_" + col.ToString(), false).FirstOrDefault() as Label;
if (l == null)
{
//problem... create label?
l = new Label() { Text = "X or O" }; //the position of this need to be set (the default is 0,0)
this.Controls.Add(l);
}
else
{
l.Text = "X or O";
}
Your board stores integers, which is an internal representation of your game state. You can create a UniformGrid that holds Label for your game GUI. The code below returns a grid based on your current board. You need to add this returned grid to your MainWindow (or whatever you use) to see it.
private UniformGrid fillLabels(int[,] board)
{
int numRow = board.GetLength(0);
int numCol = board.GetLength(1);
UniformGrid g = new UniformGrid() { Rows = numRow, Columns = numCol };
for (int i = 0; i < numRow; i++)
{
for (int j = 0; j < numCol; j++)
{
Label l = new Label();
l.Content = (board[i, j] == 0) ? "O" : "X";
Grid.SetRow(l, i);
Grid.SetColumn(l, j);
g.Children.Add(l);
}
}
return g;
}
First, do not re-create (and re-initialize) Random each time you need it: it makes generated sequences skewed badly:
private static Random s_Rand = new Random();
Try not implement algorithm in the button enent directly, it's a bad practice:
private void CreateField() { ... }
private void newBTN_Click(object sender, EventArgs e) {
CreateField();
}
putting all together:
private static Random s_Rand = new Random();
private void ApplyLabelText(String name, String text, Control parent = null) {
if (null == parent)
parent = this;
Label lb = parent as Label;
if ((lb != null) && (String.Equals(name, lb.Name))) {
lb.Text = text;
return;
}
foreach(Control ctrl in parent.Controls)
ApplyLabelText(name, text, ctrl);
}
private void CreateField() {
for (Char row = 'a'; row <= 'c'; ++row)
for (int col = 1; col <= 3; ++col)
ApplyLabelText(row.ToString() + col.ToString(), s_Rand.Next(1) == 0 ? "O" : "X");
}
private void newBTN_Click(object sender, EventArgs e) {
CreateField();
}
How about you skip the INTEGER board and go directly to a Label array?
You can then do the following to loop trough all of them:
Label[,] listOfLabels; // Do also initialize this.
foreach(Label current in listOfLabels)
{
current.Text = _rand.Next(2) == 0 ? "0" : "X";
}
I dont know how to show clearly, so:
Example - I create an array of button like this:
Button[,] _button = new Button[3, 3];
public MainPage()
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
_button[i, j] = new Button();
_button[i, j].Name = "btn" + i.ToString() + j.ToString();
_button[i, j].Tag = 0;
//Add Click event Handler for each created button
_button[i, j].Click += _button_Click;
boardGrid.Children.Add(_button[i, j]);
Grid.SetRow(_button[i, j], i);
Grid.SetColumn(_button[i, j], j);
}
} // end MainPage()
private void _button_Click(object sender, RoutedEventArgs e)
{
Button b = (Button)sender;
if (...)
b.Tag = 1;
else
b.Tag = 2;
}// end Click Event
Now how can I compare the Tag of 2 buttons in that array like:
b[1,1].Tag == b[1,2].Tag ? ...<do st>... : ....<do st>...
If you need to find position of control in array consider to set Control.Tag to that position instead of search:
_button[i, j].Tag = new System.Drawing.Point{ X = j, Y = i};
And instead of searching just
Point position = (Point)((Button)sender).Tag;
Or if you need more information (like Position + 0/x/empty choice you have) - have custom class to hold all information you need:
enum CellState { Empty, Player1, Player2 };
class TicTacToeCell
{
public Point Position {get;set;}
public CellState State {get;set;}
}
Now when you have position and state - use _buttons array to index access other ones:
Check same row:
Point position = (Point)((Button)sender).Tag;
int player1CountInThisRow = 0;
for (var col = 0; col < 3; col++)
{
if (((TicTacToeCell)(_button[position.Y, col].Tag).State == CellState.Player1)
{
player1CountInThisRow ++;
}
}
This is more of a long-winded clarification than a definite answer, but it may uncover what you're really trying to do:
In the code that you show b is (presumably) a single Button, not an array of buttons. Do you mean:
_button[1,1].Tag == _button[1,2].Tag ? ...<do st>... : ....<do st>...
Or are you trying to compare b (the event sender) to a button relative to it in the array?
I have been trying to create a chess strategy application. I seem to be having issues with trying to get the label1 control to populate during run time. I am actually pretty new to the idea of dynamically creating events like 'mouse enter, mouse leave' How do I get the label to show the coordinates in the mouse enter event
int currentXposition, currentYposition;
const string positionLabel = "Current Position: ";
private void Test_Load(object sender, EventArgs a)
{
var temp=Color.Transparent; //Used to store the old color name of the panels before mouse events
var colorName = Color.Red; //Color used to highlight panel when mouse over
int numBlocks = 8; //Used to hold the number of blocks per row
int blockSize=70;
//Initialize new array of Panels new
string[,] Position = new string[8, 8];
Panel[,] chessBoardPanels = new Panel[numBlocks, numBlocks];
string Alphabet = "A,B,C,D,E,F,G,H";
string Numbers ="1,2,3,4,5,6,7,8";
string[] alphaStrings = Numbers.Split(',');
string[] numStrings=Numbers.Split(',');
// b = sub[0];
int FirstValue, SecondValue;
//Store Position Values
for (int firstValue = 0; firstValue < 8; ++firstValue)
{
FirstValue = Alphabet[firstValue];
for (int SecValue = 0; SecValue < 8; ++SecValue)
{
SecondValue = Numbers[SecValue];
Position[firstValue, SecValue] = alphaStrings[firstValue] + numStrings[SecValue];
}
}
//Loop to create panels
for (int iRow = 0; iRow < numBlocks; iRow++)
for (int iColumn = 0; iColumn < numBlocks; iColumn++)
{
Panel p = new Panel();
//set size
p.Size = new Size(blockSize, blockSize);
//set back colour
p.BackColor = (iRow + (iColumn % 2)) % 2 == 0 ? Color.Black : Color.White;
//set location
p.Location = new Point(blockSize *iRow+15, blockSize * iColumn+15);
chessBoardPanels[iRow, iColumn] = p;
chessBoardPanels[iRow,iColumn].MouseEnter += (s,e) =>
{
currentXposition = iRow;
currentYposition = iColumn;
var oldColor = (s as Panel).BackColor;
(s as Panel).BackColor = colorName;
temp = oldColor;
label1.Text = Position[iRow, iColumn];
};
chessBoardPanels[iRow, iColumn].MouseLeave += (s, e) =>
{
(s as Panel).BackColor = temp;
};
groupBox1.Controls.Add(p);
}
}
Try this.. It was not populating because of a out of range.. iRow always = 8...
Add this class to your project.
public class ChessSquare
{
public string Letter { get; set; }
public int Number { get; set; }
public Color Color { get; set; }
public string Position
{
get { return string.Format("{0}{1}", Letter, Number); }
}
public ChessSquare()
{
}
public ChessSquare(string letter, int number)
{
Letter = letter;
Number = number;
}
}
Replace the FormLoad method with this:
int blockSize = 20;
Panel[,] chessBoardPanels = new Panel[8, 8];
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
ChessSquare sq = new ChessSquare(((char)(65+i)).ToString(), j);
sq.Color = (i + (j % 2)) % 2 == 0 ? Color.AliceBlue : Color.White;
Panel p = new Panel()
{ Size = new Size(blockSize, blockSize),
BackColor = sq.Color,
Tag = sq,
Location = new Point(blockSize * i + 15, blockSize * j+15),
};
p.MouseEnter+=new EventHandler(squareMouseEnter);
p.MouseLeave += new EventHandler(squareMouseLeave);
chessBoardPanels[i, j] = p;
groupBox1.Controls.Add(p);
}
}
And add those two methods to your code:
void squareMouseEnter(object sender, EventArgs e)
{
Panel p = (Panel)sender;
ChessSquare sq = (ChessSquare)p.Tag;
p.BackColor = Color.Aqua;
label1.Text = string.Format("Current position: {0}", sq.Position);
}
void squareMouseLeave(object sender, EventArgs e)
{
Panel p = (Panel) sender;
ChessSquare sq = (ChessSquare)p.Tag;
p.BackColor = sq.Color;
}
I there are several ways of doing it... This one is pretty straight forward.
Here is the code I'm using
It's supposed to create labels for each module name entered by a user and all the assessment names for that module under it. Should work with any number of modules and assessments. The problem is that it only shows assessments for the last module displayed.
dat.Modules and dat.Assessments are arraylists, each of them holds 4 elements with info about a module or an assessments, that is why i divide the count by 4.
private void testingButton_Click(object sender, EventArgs e)
{
int pos1 = 50;
int pos2 = 150;
int modLength = dat.Modules.Count;
modLength = modLength / 4;
int assessLength = 0;
int arrayData = 0;
int displayCount = 0;
for (int i = 0; i < modLength; i++)
{
this.moduleLabels.Add(new Label());
System.Drawing.Point pLabel1 = new System.Drawing.Point(50, pos1);
(moduleLabels[i] as Label).Location = pLabel1;
(moduleLabels[i] as Label).Size = new System.Drawing.Size(100, 13);
(moduleLabels[i] as Label).Text = dat.Modules[arrayData].ToString();
tabPage5.Controls.Add((moduleLabels[i] as Label));
String asd = dat.Modules[arrayData + 2].ToString();
Console.WriteLine(asd);
assessLength = int.Parse(asd);
pos2 = pos1 + 25;
for (int y = 0; y < assessLength; y++)
{
this.assessLabels.Add(new Label());
System.Drawing.Point pLabel2 = new System.Drawing.Point(70, pos2);
(assessLabels[y] as Label).Location = pLabel2;
(assessLabels[y] as Label).Size = new System.Drawing.Size(250, 13);
(assessLabels[y] as Label).Text = dat.Assessments[displayCount + 1].ToString() + " weights " + dat.Assessments[displayCount+2].ToString() +"%, Enter your mark:";
textboxComputer.Add(new TextBox());
System.Drawing.Point pText1 = new System.Drawing.Point(400, pos2);
(textboxComputer[y] as TextBox).Location = pText1;
(textboxComputer[y] as TextBox).Size = new System.Drawing.Size(20, 20);
tabPage5.Controls.Add(assessLabels[y] as Label);
tabPage5.Controls.Add(textboxComputer[y] as TextBox);
pos2 = pos2 + 25;
displayCount = displayCount + 4;
}
pos1 = pos2+25;
arrayData = arrayData + 4;
}
}
this is an example of what it displays
http://dc540.4shared.com/download/YI8IENYI/tsid20120501-211723-cbc785f9/asd.jpg
The first two modules should have their assessments listed. The first one doesn't display any. For Java it only displays the last one, out of 3 total for that module. And for the last Module "Another Module" it displays all assessments.
For each increment of i, y starts at 0. You then add new labels to assessLabels, but attempt to access the one you added by using assessLabels[y] which would usually yield the labels created for the previous value of i. This causes labels created for the first module to be reused by the next, and so forth.
A quick solution is not to use assessLabels[y] but assessLabels[assessLabels.Count - 1].
A better solution is to create a local variable for the labels, set their properties, and then add them to the list:
for (int y = 0; y < assessLength; y++)
{
Label assessLabel = new Label();
assessLabel.Location = ...;
// etc.
tabPage5.Controls.Add(assessLabel);
assessLabels.Add(assessLabel);
}
This would also remove the need to continuously cast the ArrayList members and unneeded access to the list.
PS. If assessLabels only contains objects of type Labels, consider using a List<Label> instead.
Is there a way to create 3 rows with 3 colums with labels (or similar) to create a 2d "map" with data to manipulate in an easy way?
just placing 9 labels is easy but I want each labels to be accessed with the same array.
How it looks like in the form:
label1 label2 label3
label4 label5 label6
label7 label8 label9
If i need to change the property of label5 I would like to access it something like this:
labelarray[1][1].Text = "Test";
(labelarray[row][column].Property )
How do I do this?
Or could this be achieved in another way?
class Data
{
private string text;
public string Text
{
get { return text; }
set { text = value; }
}
}
class Program
{
static void Main(string[] args)
{
Data[,] map = new Data[3, 3];
map[1, 1] = new Data();
map[1, 1].Text = "Test";
}
}
Edit: fixed error.
private void button1_Click(object sender, EventArgs e)
{
string[] nine_labels = { "a", "b", "c", "d", "e", "f", "g", "h", "i" };
var labelarray= new Label[3,3];
// putting labels into matrix form
int c = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
var lbl = new Label();
lbl.Text = nine_labels[c];
lbl.Top = i * 100;
lbl.Left = j * 100;
labelarray[i, j] = lbl;
c++;
}
}
// adding labels to form
foreach (var item in labelarray)
{
this.Controls.Add(item);
}
// test
labelarray[1, 1].Text = "test";
}
NOTE: You'll need to add one button and call this function on Click of that button.
The answer from tehMick actually lead to a runtime exception in .NET 2.0 but, except for that, the example is straight to the point.
The types of the two dimensional array must have a public accessible property, so you can access it directly as you said:
public class DTO
{
private String myStrProperty;
public String MyStrProperty
{
get {return myStrProperty; }
set { myStrProperty = value; }
}
public DTO(string myStrProperty)
{
this.myStrProperty = myStrProperty;
}
}
class Program
{
private static Logger logger;
static void Main(string[] args)
{
DTO[,] matrix =
{
{new DTO("label1"), new DTO("label2")},
{new DTO("label3"), new DTO("label4")}
};
matrix[0, 1].MyStrProperty = "otherValue";
}
}
Here's an example that's specific to winforms. Hopefully it will answer the question a little bit better:
const int spacing = 50;
Label[][] map = new Label[3][];
for (int x = 0; x < 3; x++)
{
map[x] = new Label[3];
for (int y = 0; y < 3; y++)
{
map[x][y] = new Label();
map[x][y].AutoSize = true;
map[x][y].Location = new System.Drawing.Point(x * spacing, y * spacing);
map[x][y].Name = "map" + x.ToString() + "," + y.ToString();
map[x][y].Size = new System.Drawing.Size(spacing, spacing);
map[x][y].TabIndex = 0;
map[x][y].Text = x.ToString() + y.ToString();
}
this.Controls.AddRange(map[x]);
}
map[1][1].Text = "Test";