将参数传递给事件处理程序
Passing parameters to the Event handler
背景:
对于一项学校作业,我正在制作一款益智游戏。玩家必须创建一个益智游戏并将其保存在文本文件中。首先,玩家输入行和列的图块 (PictureBoxes),程序创建 2d PictureBox 布局。之后玩家选择工具(从 ImageList 分配图像的按钮)并单击图块,图像出现在图块上。
方法:我有一个自定义 class,它继承自 PictureBox class 并具有 属性 ToolValue。 ToolValue 是玩家选择并添加到该 PictureBox 的工具(常量分配给工具)。为了加载图像,我在 for 循环内创建了一个新的事件处理程序,用于处理 PictureBox 控件的单击事件并具有其他参数。新的事件处理程序将图像加载到磁贴上,并将自定义 PictureBox(MyPictureBox) class 的 ToolValue 属性 class 设置为常量。我创建了一个 PictureBox class 引用的二维数组。
MyPictureBox[,] Tile;
public void DrawALineOfPictureBoxes(int rowNumber, int columnCount, int rowCount, int leftPosition, int topPosition, int height, int width)
{
Tile = new MyPictureBox[rowCount, columnCount];
for (int colNumber = 0; colNumber < columnCount; colNumber++)
{
Tile[rowNumber, colNumber] = new MyPictureBox();
Tile[rowNumber, colNumber].Left = leftPosition;
Tile[rowNumber, colNumber].Top = topPosition;
Tile[rowNumber, colNumber].Height = height;
Tile[rowNumber, colNumber].Width = width;
Tile[rowNumber, colNumber].BorderStyle = BorderStyle.Fixed3D;
Tile[rowNumber, colNumber].SizeMode = PictureBoxSizeMode.StretchImage;
Tile[rowNumber, colNumber].ToolValue = 0;
Tile[rowNumber, colNumber].Click += new EventHandler((sender,e) => LoadImage_Click(sender, e, rowNumber, colNumber));
this.Controls.Add(Tile[rowNumber, colNumber]);
leftPosition += width;
}
}
/// <summary>
/// Method executes when Generate Button is clicked
/// Method invokes DrawALineOfPictureBoxes method which generates a row of Picture boxes
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
///
private void PictureBoxGenerate_Click(object sender, EventArgs e)
{
try
{
int numRows = int.Parse(txtRowCount.Text);
int numColumns = int.Parse(txtColumnCount.Text);
int leftPos = 400;
int topPos = 120;
int height = 100;
int width = 100;
//loop after each row of picturebox is generated
for (int rowNumber = 0; rowNumber < numRows; ++rowNumber)
{
DrawALineOfPictureBoxes(rowNumber, numColumns, numRows, leftPos, topPos, height, width);
topPos += height;
}
}
catch (FormatException)
{
MessageBox.Show("Please provide valid data for rows and columns (Both must be integers)","Sokoban", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// Method is executed when PictureBox cell is clicked and load image to that picture box through resources
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LoadImage_Click(object sender, EventArgs e, int rowNumber, int colNumber)
{
string Content = rowNumber.ToString() + "," + colNumber.ToString();
Console.WriteLine(Content);
MyPictureBox pictureBox = sender as MyPictureBox;
switch (imageType)
{
case ImageType.None:
//Empty the pictureBox Cell
pictureBox.Image = null;
ToolVal = 0;
break;
case ImageType.Hero:
pictureBox.Image = Properties.Resources.Hero;
ToolVal = 1;
break;
case ImageType.Wall:
pictureBox.Image = Properties.Resources.Wall;
ToolVal = 2;
break;
case ImageType.Box:
pictureBox.Image = Properties.Resources.Box;
ToolVal = 3;
break;
case ImageType.Destination:
pictureBox.Image = Properties.Resources.Destination;
ToolVal = 4;
break;
default:
break;
}
//assigning values to Tile array
Tile[rowNumber,colNumber].ToolValue = ToolVal;
}
问题:我已经将被点击的pictureBox的行号和列号作为参数传递给加载图像的EventHandler。在事件处理程序内部,切换循环检查单击了哪个工具,并相应地将常量分配给 toolValue 属性。如果我单击第一个图片框,则传递给事件处理程序的参数对于 rowNumber 为 0,对于 colNumber 为 0。事件处理程序参数应该相同,但 colNumber 参数不是 0,而是 3,即。生成的 2d pictureBoxes 的总列数。
这是输出>>Colnumber is 3
您的问题在以下行:
Tile[rowNumber, colNumber].Click += new EventHandler((sender,e) => LoadImage_Click(sender, e, rowNumber, colNumber));
您正在使用传递 rowNumber 和 colNumber 的 lambda 表达式。
当触发图块的 Click 事件时,将执行 lambda 并传递当时 rowNumber 和 colNumber 的值(而不是它们在创建事件处理程序时的值)。因为您的循环有 运行 完成,所以您传递的 colNumber 的值将始终为 3.
因此您需要另一种机制来计算被点击的 Tile 的行和列。
您可以根据传递的发件人执行此操作:将其转换为 Tile,检索其位置并从中计算行和列。
如果您真的想传递实际的列号,您总是可以像这样“捕获 'colNumber' 的值:
Tile = new MyPictureBox[rowCount, columnCount];
for (int colNumber = 0; colNumber < columnCount; colNumber++)
{
int actualColumn = colNumber;
int actualRow = rowNumber;
Tile[rowNumber, colNumber] = new MyPictureBox();
...
Tile[rowNumber, colNumber].Click += new EventHandler((sender,e) => LoadImage_Click(sender, e, actualRow, actualColumn));
this.Controls.Add(Tile[rowNumber, colNumber]);
leftPosition += width;
}
对于创建图块的循环的每次迭代,您都会创建一个新变量,其中包含图块的实际行和列。这些值不会改变,因为每次迭代都有自己的版本。你传递这些变量而不是 rowNumber & colNumber.
我可以给你一般性的建议:
首先,WindowsForms 和其他 GUI 技术并不是游戏开发的正确工具。它适用于纯回合制、单人或热门多人游戏。如果你不超过图形。基本上,旧版 Solitair 是关于可能性的上限。对于认真的开发,你需要一些带有游戏循环的东西。这是一个 school/learning 项目,因此它实际上可能有点复杂。
其次,避免在UI中存储数据。在某些集合的后面的代码中总是有数据(游戏状态)。 UI 只是该数据的 表示 ,您会定期更新。
至于传递给事件的参数,这里最有用的应该是sender
。这是引发事件的实例。您可以按原样进行检查:
//Shared handler for OK, Apply and Cancel Buttons
if(sender == btnOK || sender == btnApply){
}
if(sender == btnOK || sender == btnCancel){
}
或者您可以转换它,以获得对发件人属性的完全访问权限
PictureBox source = (PictureBox)sender;
source.ImageLocataion = "clicked.png";
一个特别的 属性 是 Tag。它需要一个对象,它的目的似乎是在 name/reference 单独不起作用的情况下识别您到达此处的哪个 UI 元素。 number/Identifier 之类的东西应该在这里有所帮助。或数据库行的主键。
背景: 对于一项学校作业,我正在制作一款益智游戏。玩家必须创建一个益智游戏并将其保存在文本文件中。首先,玩家输入行和列的图块 (PictureBoxes),程序创建 2d PictureBox 布局。之后玩家选择工具(从 ImageList 分配图像的按钮)并单击图块,图像出现在图块上。
方法:我有一个自定义 class,它继承自 PictureBox class 并具有 属性 ToolValue。 ToolValue 是玩家选择并添加到该 PictureBox 的工具(常量分配给工具)。为了加载图像,我在 for 循环内创建了一个新的事件处理程序,用于处理 PictureBox 控件的单击事件并具有其他参数。新的事件处理程序将图像加载到磁贴上,并将自定义 PictureBox(MyPictureBox) class 的 ToolValue 属性 class 设置为常量。我创建了一个 PictureBox class 引用的二维数组。
MyPictureBox[,] Tile;
public void DrawALineOfPictureBoxes(int rowNumber, int columnCount, int rowCount, int leftPosition, int topPosition, int height, int width)
{
Tile = new MyPictureBox[rowCount, columnCount];
for (int colNumber = 0; colNumber < columnCount; colNumber++)
{
Tile[rowNumber, colNumber] = new MyPictureBox();
Tile[rowNumber, colNumber].Left = leftPosition;
Tile[rowNumber, colNumber].Top = topPosition;
Tile[rowNumber, colNumber].Height = height;
Tile[rowNumber, colNumber].Width = width;
Tile[rowNumber, colNumber].BorderStyle = BorderStyle.Fixed3D;
Tile[rowNumber, colNumber].SizeMode = PictureBoxSizeMode.StretchImage;
Tile[rowNumber, colNumber].ToolValue = 0;
Tile[rowNumber, colNumber].Click += new EventHandler((sender,e) => LoadImage_Click(sender, e, rowNumber, colNumber));
this.Controls.Add(Tile[rowNumber, colNumber]);
leftPosition += width;
}
}
/// <summary>
/// Method executes when Generate Button is clicked
/// Method invokes DrawALineOfPictureBoxes method which generates a row of Picture boxes
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
///
private void PictureBoxGenerate_Click(object sender, EventArgs e)
{
try
{
int numRows = int.Parse(txtRowCount.Text);
int numColumns = int.Parse(txtColumnCount.Text);
int leftPos = 400;
int topPos = 120;
int height = 100;
int width = 100;
//loop after each row of picturebox is generated
for (int rowNumber = 0; rowNumber < numRows; ++rowNumber)
{
DrawALineOfPictureBoxes(rowNumber, numColumns, numRows, leftPos, topPos, height, width);
topPos += height;
}
}
catch (FormatException)
{
MessageBox.Show("Please provide valid data for rows and columns (Both must be integers)","Sokoban", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// Method is executed when PictureBox cell is clicked and load image to that picture box through resources
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LoadImage_Click(object sender, EventArgs e, int rowNumber, int colNumber)
{
string Content = rowNumber.ToString() + "," + colNumber.ToString();
Console.WriteLine(Content);
MyPictureBox pictureBox = sender as MyPictureBox;
switch (imageType)
{
case ImageType.None:
//Empty the pictureBox Cell
pictureBox.Image = null;
ToolVal = 0;
break;
case ImageType.Hero:
pictureBox.Image = Properties.Resources.Hero;
ToolVal = 1;
break;
case ImageType.Wall:
pictureBox.Image = Properties.Resources.Wall;
ToolVal = 2;
break;
case ImageType.Box:
pictureBox.Image = Properties.Resources.Box;
ToolVal = 3;
break;
case ImageType.Destination:
pictureBox.Image = Properties.Resources.Destination;
ToolVal = 4;
break;
default:
break;
}
//assigning values to Tile array
Tile[rowNumber,colNumber].ToolValue = ToolVal;
}
问题:我已经将被点击的pictureBox的行号和列号作为参数传递给加载图像的EventHandler。在事件处理程序内部,切换循环检查单击了哪个工具,并相应地将常量分配给 toolValue 属性。如果我单击第一个图片框,则传递给事件处理程序的参数对于 rowNumber 为 0,对于 colNumber 为 0。事件处理程序参数应该相同,但 colNumber 参数不是 0,而是 3,即。生成的 2d pictureBoxes 的总列数。
这是输出>>Colnumber is 3
您的问题在以下行:
Tile[rowNumber, colNumber].Click += new EventHandler((sender,e) => LoadImage_Click(sender, e, rowNumber, colNumber));
您正在使用传递 rowNumber 和 colNumber 的 lambda 表达式。 当触发图块的 Click 事件时,将执行 lambda 并传递当时 rowNumber 和 colNumber 的值(而不是它们在创建事件处理程序时的值)。因为您的循环有 运行 完成,所以您传递的 colNumber 的值将始终为 3.
因此您需要另一种机制来计算被点击的 Tile 的行和列。 您可以根据传递的发件人执行此操作:将其转换为 Tile,检索其位置并从中计算行和列。
如果您真的想传递实际的列号,您总是可以像这样“捕获 'colNumber' 的值:
Tile = new MyPictureBox[rowCount, columnCount];
for (int colNumber = 0; colNumber < columnCount; colNumber++)
{
int actualColumn = colNumber;
int actualRow = rowNumber;
Tile[rowNumber, colNumber] = new MyPictureBox();
...
Tile[rowNumber, colNumber].Click += new EventHandler((sender,e) => LoadImage_Click(sender, e, actualRow, actualColumn));
this.Controls.Add(Tile[rowNumber, colNumber]);
leftPosition += width;
}
对于创建图块的循环的每次迭代,您都会创建一个新变量,其中包含图块的实际行和列。这些值不会改变,因为每次迭代都有自己的版本。你传递这些变量而不是 rowNumber & colNumber.
我可以给你一般性的建议:
首先,WindowsForms 和其他 GUI 技术并不是游戏开发的正确工具。它适用于纯回合制、单人或热门多人游戏。如果你不超过图形。基本上,旧版 Solitair 是关于可能性的上限。对于认真的开发,你需要一些带有游戏循环的东西。这是一个 school/learning 项目,因此它实际上可能有点复杂。
其次,避免在UI中存储数据。在某些集合的后面的代码中总是有数据(游戏状态)。 UI 只是该数据的 表示 ,您会定期更新。
至于传递给事件的参数,这里最有用的应该是sender
。这是引发事件的实例。您可以按原样进行检查:
//Shared handler for OK, Apply and Cancel Buttons
if(sender == btnOK || sender == btnApply){
}
if(sender == btnOK || sender == btnCancel){
}
或者您可以转换它,以获得对发件人属性的完全访问权限
PictureBox source = (PictureBox)sender;
source.ImageLocataion = "clicked.png";
一个特别的 属性 是 Tag。它需要一个对象,它的目的似乎是在 name/reference 单独不起作用的情况下识别您到达此处的哪个 UI 元素。 number/Identifier 之类的东西应该在这里有所帮助。或数据库行的主键。