I have problem with my C# WinForms project. I have a function that should change the place of buttons if they touch each other. For example, if I have btn1 at oldloction = (4,2) and btn2 at oldlocaction (2,6), then if I will move the buttons and they will touch bt1 new location = (2,6) and bt2 new location = (4,2)
now i did that with 2 buttons and it works.
locationx - means the x location on the button and its orgenize firat place of the location feat to the first buttons[0], the second feat to locationx[1] = buttons[1].location.x;
location - works the same ass locationx but uts the y locaion.
private void myText_MouseUp(object sender, MouseEventArgs e)
{
Point oldlocation = new Point(locationx[0], locationy[0]);
Point oldlocation2 = new Point(locationx[1], locationy[1]);
if (buttons[0].Location.Y == buttons[1].Location.Y)
{
buttons[1].Location = oldlocation;
buttons[0].Location = oldlocation2;
}
}
When I tried to make that as a global function it doesn't work and I don't know why.
This is the code of the global function that doesn't work:
private void myText_MouseUp(object sender, MouseEventArgs e)
{
for (int i = 0; i < counter; i++)
{
Point oldlocation = new Point(locationx[i], locationy[i]);
for (int j = 0; j < counter; j++)
{
if (i != j)
{
Point oldlocation2 = new Point(locationx[j], locationy[j]);
if (buttons[i].Location.Y != buttons[j].Location.Y)
{
buttons[j].Location = oldlocation2;
buttons[i].Location = oldlocation;
}
else if (buttons[i].Location.Y == buttons[j].Location.Y)
{
buttons[j].Location = oldlocation;
buttons[i].Location = oldlocation2;
}
}
}
}
}
Try using the button event for when it is pressed to call the function, rather than creating your own.
If the second function is not part of the control or form containing the buttons, it won't have a way to access the buttons array or locationx and locationy. You may need to pass these values as arguments to your function or ensure that they are provided as members of the class containing the second function. Note that generally a utility function would not take in "sender" and "MouseEventArgs" - pass only the specific data that the utility function needs to do its job.
Related
I need to add PictureBox's (pictureBox11 to pictureBox30) to an array.
So instead of adding PictureBox's like this:
PictureBox[] Coins = new PictureBox[20];
Coins[0] = pictureBox11;
...
Coins[19] = pictureBox30;
I wrote a for cycle like this (DOESN'T WORK) :
for (int i = 11; i < 31; i++)
{
for (int j = 0; j < Coins.Length; j++)
{
Coins[j] = (PictureBox)Controls.Find(
"pictureBox" + i.ToString(), true)[0];
}
}
There might be a small stupid mistake somewhere because I use the same cycle for another thing and it works, idk, maybe I'm just blind and cant see the mistake.
Maybe it is relevant, so I will include the code I am assigning for the array elements:
for (int i = 0; i < Coins.Length; i++)
{
if (player.Bounds.IntersectsWith(Coins[i].Bounds))
{
Coins[i].Visible = false;
}
}
EVERYTHING works fine if I add them as shown in first code, but it is not very practical.
Why isn't the second code (the for cycle) I wrote working for me?
Is there a better way to add multiple pictureboxes to an array?
I am guessing you are making this more complicated than it has to be. To try and understand better, I assume that when you say I need to add pictureboxes (pictureBox11 to pictureBox30) to an array. that you mean there are 30+ PictureBoxs on your form and each PictureBox uses a naming convention such that each is named “pictureBoxX” where “X” is 1,2,3…30,31. Then you want to get a (consecutive?) group of “PictureBoxes” on the form to make invisible. I hope this is correct.
To simply make the picture boxes invisible, I do not think an array is needed. Simply loop through the picture boxes and make it invisible if the name matches a string of the form “pictureBoxX “. I used IndexsAreValid method to validate the start and end indexes. It is also used in the code for an array implementation below this code.
Make PictureBoxes invisible without an array
private void SetPictureBoxesInvisible(int start, int end) {
int size = -1;
string targetString = "";
if (IndexsAreValid(start, end, out size)) {
for (int i = start; i < end + 1; i++) {
try {
targetString = "pictureBox" + i;
PictureBox target = (PictureBox)Controls.Find(targetString, true)[0];
if (target != null) {
target.Visible = false;
}
}
catch (IndexOutOfRangeException e) {
return;
}
}
}
}
If you must have a PictureBox array returned, then the code below should work.
First, to get an array of PictureBoxs as you want you need an array to store them. But first you need to know how big to make it. From your posted code it appears that you want to get picture boxes 11-30 and put them in an array. So we can get the size from these numbers… i.e. 30-11=19 +1 = 20. That’s about all you need. Simply create the array and loop through all the picture boxes and grab pictureBox11-pictureBox30. When done we can use this array to make these `PictureBoxes” invisible.
I created a method IsValidPic similar to a tryParse to validate if the given index (1,2,3..30) is valid. If it is out of range, I simply ignore that value. This gives you the ability to grab an individual picture box in case the desired picture boxes are not contiguous. I used a few buttons to test the methods.
Hope this helps.
private PictureBox[] GetPictureBoxes(int start, int end) {
int size = - 1;
if (IndexsAreValid(start, end, out size)) {
PictureBox curPic = null;
PictureBox[] allPics = new PictureBox[size];
int index = 0;
for (int i = start; i <= end; i++) {
if (IsValidPic(i, out curPic)) {
allPics[index] = curPic;
index++;
}
}
return allPics;
}
else {
return new PictureBox[0];
}
}
private Boolean IndexsAreValid(int start, int end, out int size) {
if (start < 1 || end < 1) {
size = -1;
return false;
}
if (start > end) {
size = -1;
return false;
}
size = end - start + 1;
return true;
}
private Boolean IsValidPic(int index, out PictureBox picture) {
string targetName = "pictureBox" + index;
try {
PictureBox target = (PictureBox)Controls.Find(targetName, true)[0];
if (target != null) {
picture = target;
return true;
}
picture = null;
return false;
}
catch (IndexOutOfRangeException e) {
picture = null;
return false;
}
}
private void ResetAll() {
foreach (PictureBox pb in this.Controls.OfType<PictureBox>()) {
pb.Visible = true;
}
}
private void button1_Click(object sender, EventArgs e) {
TurnInvisible(2, 3);
}
private void button3_Click(object sender, EventArgs e) {
TurnInvisible(11, 30);
}
private void button4_Click(object sender, EventArgs e) {
TurnInvisible(1,7);
}
private void TurnInvisible(int start, int end) {
PictureBox[] pictureBoxesToChange = GetPictureBoxes(start, end);
foreach (PictureBox pb in pictureBoxesToChange) {
if (pb != null)
pb.Visible = false;
}
}
private void button2_Click(object sender, EventArgs e) {
ResetAll();
}
This question already has answers here:
Pass extra parameters to an event handler?
(10 answers)
Closed 2 years ago.
I use the loop for a two dimensions array of buttons. I don't know how to know exactly which buttons in array were clicked or not
Here are my code:
for (int i = 0; i < 100 ; i++)
{
for (int j=0 ; j< 100; i++)
{
arrButton[i, j] = new Button();
arrButton[i,j].Size = new Size(size1button, size1button);
arrButton[i,j].Location = new Point(j*size1button, i*size1button);
arrButton.Click += new EventHandler(arrButton_Click);
}
}
Can I use parameters i, j for mouse click event like:
private void arrButton_Click(object sender, EventArgs e, int i, int j)
{
//my idea : add i, j to another int[,] array to keep track of buttons which were clicked
}
If this exits, how to write it correctly? Or can you recommend or method to know exactly where the button was clicked in array ?
Try this
public class Indeces
{
public int IndexI { get; set; }
public int IndexJ { get; set; }
}
Now in loop set Tag
for (int i = 0; i < 100 ; i++)
{
for (int j=0 ; j< 100; i++)
{
arrButton[i, j] = new Button();
arrButton[i,j].Size = new Size(size1button, size1button);
arrButton[i,j].Location = new Point(j*size1button, i*size1button);
arrButton.Click += new EventHandler(arrButton_Click);
arrButton.Tag = new Indeces {IndexI = i,IndexJ = j};
}
}
Get values from Tag here as
private void arrButton_Click(object sender, EventArgs e)
{
var button = sender as Button;
var indeces = (Indeces) button.Tag;//get indeces here
var i = indeces.IndexI;
var j = indeces.IndexJ;
}
You cannot change the EventHandler signature to include your i and j.
However, you can get that information from what is already passed to the arrButton_Click method. Since you set the location of each button as new Point(j*size1button, i*size1button), you can get each i and j component back by dividing the location of your button by size1button.
To get that location, you can use the sender, which is your Button (a cast is necessary):
private void arrButton_Click(object sender, EventArgs e)
{
Button btnClicked = (Button) sender;
int i = btnClicked.Location.Y / size1button;
int j = btnClicked.Location.X / size1button;
}
Also, the code you're currently using to create the buttons have a couple errors.
First, you're never incrementing j; the second loop does i++.
Second, if you want your buttons to appear, you have to add them to your Form's Controls.
Finally, I don't think you can have 10 000 active buttons on your form, try a lower number, like 25.
So the corrected code would look like:
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
arrButton[i, j] = new Button();
arrButton[i, j].Size = new Size(size1button, size1button);
arrButton[i, j].Location = new Point(j*size1button, i*size1button);
arrButton[i, j].Click += arrButton_Click;
Controls.Add(arrButton[i,j]);
}
}
You can also notice that I removed your declaration of new EventHandler, which was redundant.
If you are only interested in Location then whats wrong with this? -
private void arrButton_Click(object sender, EventArgs e)
{
var button = sender as Button;
//now use button.Location
}
But if you want more data other than just location. Here is an example
Use a custom button class -
public class CustomButton<T> : Button {
public T Data{get;set;}
public CustomButton(T data){
this.Data = data; //i didn't compile it, so data type might mismatch.
}
}
Then use this button class -
for (int i = 0; i < 100 ; i++)
{
for (int j=0 ; j< 100; i++)
{
arrButton[i, j] = new CustomButton<T> (...some data);
arrButton[i,j].Size = new Size(size1button, size1button);
arrButton[i,j].Location = new Point(j*size1button, i*size1button);
arrButton.Click += new EventHandler(arrButton_Click);
}
}
In the event handler Cast to CustomButton and voila, there is your location -
private void arrButton_Click(object sender, EventArgs e)
{
var cButton = sender as CustomButton<T>;
// cButton.Datais your point. Have fun
}
BTW, you cannot change the default signature of event handlers, if you want you have to implement your own event/delegate.
So in my program i created a struct with a button and a number value... like this
struct box
{
public int numberValue;
public Button button;
}
I then made a 2D array of this struct
box[,] boxes = new box[20, 20];
Now what i did was make 400 buttons and assigned them to each index of the array... like this
private void createBoxes()
{
int positionX;
int positionY;
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
positionX = 20 + (25 * i);
positionY = 20 + (25 * j);
boxes[i, j].button = new System.Windows.Forms.Button();
boxes[i, j].button.Location = new System.Drawing.Point(positionX,positionY);
boxes[i, j].button.Size = new System.Drawing.Size(25, 25);
this.Controls.Add(boxes[i, j].button);
boxes[i, j].button.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
boxes[i, j].button.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
boxes[i, j].button.Visible = true;
boxes[i, j].button.Name = "button";
boxes[i, j].button.Click += new EventHandler(buttonClick);
}
}
}
Now when i make the event handler i want to send "boxes[i,j]" not just "boxes[i,j].button" is there anyway to do this?
Short of defining your own anonymous event handler, there's an easy way to do what you want:
boxes[i, j].button.Tag = boxes[i, j];
Then later:
private void buttonClick(object sender, EventArgs e)
{
var box = ((Button)sender).Tag as box;
}
This can be solved via an anonymous event handler.
var box = boxes[i, j]; // You must use a new variable within this scope
box.button.Click += (obj, args) => buttonClick(box, args);
This is the quickest solution with the least code. Just be aware that anonymous event handlers are notorious for hidden gotchas, and the need to assign a new box variable is an example. The following code will run, but no matter which button you press, the last-assigned values of i and j would be used within the handler.
boxes[i,j].button.Click += (obj, args) => buttonClick(boxes[i,j], args);
No, this is not possible. The individual button control is the one that raises the event, thus it is the object referenced by the sender parameter. The array that contains the button control is irrelevant.
This behavior is by-design. If you wanted to change a property of the button in response to the user clicking on it, it would be impossible to do unless you knew which individual button was clicked. Having only a reference to the array that contains all of the buttons would not provide sufficient information about the individual button that was clicked.
I know how to create button during runtime.
Button button1 = new Button();
button1.Location = new Point(20,10);
button1.Text = "Click Me";
// adding to groupBox1
groupBox1.Controls.Add(button1);
But the problem is i want to add multiple buttons like this..
for(int i = 1; i < 30; i++) {
Button button[i] = new Button();
// Button customization here...
...
groupBox1.Controls.Add(button[i]);
}
The code above is false code. How can I make this happen true in C#.net? i want to create multiple buttons with button name, button1, button2, button3, button4, .... button30;
You can't declare extra variables at execution time in C# - but you really don't want to anyway, as you wouldn't be able to access them dynamically afterwards. Just create an array:
// buttons would be declared as Button[] as a member variable
buttons = new Button[30];
for(int i = 0; i < buttons.Length; i++) {
buttons[i] = new Button();
// Button customization here...
...
groupBox1.Controls.Add(buttons[i]);
}
Alternatively, use a List<Button>, which will certainly be more convenient if you don't know how many buttons you need beforehand. (See the obligatory "arrays considered somewhat harmful" blog post.)
Of course, if you don't actually need to get at the buttons later, don't bother assigning them to anything visible outside the loop:
for(int i = 0; i < 30; i++) {
Button button = new Button();
// Button customization here...
...
groupBox1.Controls.Add(button);
}
You need to think about what information you need access to when... and how you want to access it. If you logically have a collection of buttons, you should use a collection type variable (like a list or an array).
Frankly I think it's one of the curses of the VS designers that you end up with horrible names such as "groupBox1" which carry no information beyond what's already in the type declaration, and encourage developers to think of collections of controls via individually-named variables. That's just me being grumpy though :)
Try this
for(int i = 1; i < 30; i++) {
Button button = new Button();
// Button customization here...
button.Name = "Button" + i.ToString();
groupBox1.Controls.Add(button);
}
You seem like you're almost on the right track:
// in form class
Button[] m_newButtons = new Button[30];
// in your trigger function
for(int i = 0; i < 30; ++i)
{
m_newButtons[i] = new Button();
// ...
groupBox1.Controls.Add(m_newButtons[i]);
}
If you try and do this more than once you may have to remove the old buttons from the control before adding the new ones.
buttons = new Button[30];
for(int i = 0; i < buttons.Length; i++) {
buttons[i] = new Button();
groupBox1.Controls.Add(buttons[i]);
}
this code will work but button will be added one over other so set location also
buttons = new Button[30];
for(int i = 0; i < buttons.Length; i++)
{
buttons[i] = new Button();
Point p=new Point(xvalue,yvalue);
buttons[i].Location = p;
groupBox1.Controls.Add(buttons[i]);
}
one thing you want to remember increment the x or y position by which you want to display it
Try this one out, I have just learned it myself:
public partial class Form1 : Form
{
Button[] btn = new Button[12];// <--------<<<Button Array
public Form1()
{
InitializeComponent();
}
private void Form1_Load (object sender, EventArgs e)
{
for (int i = 0; i < 12; i++)
{
btn[i] = new Button ( );
this.flowLayoutPanel1.Controls.Add(btn[i]);
}
}
// double click on the flow layoutPannel initiates this code
private void flowLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
}
}
For example I have a list of picture boxes that raise an event once the cursor hover over them.
Yet I need somehow not only to rise this event, but also to pass the "i" variable, so that I would know which picturebox has the cursor over it.
for (int i = 0; i < 25; i++)
{
....
pbList.Add(new PictureBox());
pbList[i].MouseHover += new System.EventHandler(this.beeHideInfo);
//// need to pass "i" here
}
and
private void beeShowInfo(object sender, EventArgs e)
{
lb_beeInfo.Text = "You are hovering over: "+beeList[i].name;
/// need to get this "i"
}
Any ideas?
You cannot pass variables to an event.
Besides, the necessary variable has already been passed to you: sender.
sender is a reference to the object which raised the event. In your case, it's a reference to the PictureBox that raised the event.
The object sender parameter is the PictureBox sending the event. If you need to associate something with that object, you can use its Tag member:
for (int i = 0; i < 25; i++)
{
....
pbList.Add(new PictureBox() { Tag = beeList[i] });
pbList[i].MouseHover += new System.EventHandler(this.beeHideInfo);
}
and
private void beeShowInfo(object sender, EventArgs e)
{
PictureBox pb = (PictureBox)sender;
Bee b = (Bee)pb.Tag;
lb_beeInfo.Text = "You are hovering over: "+b.name;
}
Assuming pbList and beelist contain related items in the same order, you can do something like beeList[ pbList.IndexOf(sender) ].name
You could you do something like:
for (int i = 0; i < 25; i++)
{
...
pbList.Add(new PictureBox());
var index = i;
pbList[i].MouseHover +=
delegate
{
lb_beeInfo.Text = "You are hovering over: "+beeList[index].name;
};
}
I.e. use an anonymous method.
As John Saunders says, there is an easier solution.
The easiest way to do this is use an anonymous function to explicitly pass the PictureBox or index instance into the handler.
for (int i = 0; i < 25; i++)
{
....
var box = new PictureBox();
pbList.Add(box);
box.MouseHover += delegate { this.beeShowInfo(box); }
}
private void beeShowInfo(PictureBox box)
{
lb_beeInfo.Text = "You are hovering over: "+box.Name;
}
another way might be if you create a Custom Picture Box
class CustomPictureBox : PictureBox
{
public int id;
public CustomPictureBox(int ID)
{
id = ID;
}
}
firt place a GLOBAL id to parent and each time if a CustomPicureBox is clicked get the ID
than everywhere you want to make changes to Clicked CustomPicutreBox test it
foreach(CustomPicutreBox i in Control.controls)
{
if(i.ID == sender.ID)
doWhatEveryYouWant();
}
}