I am having trouble with windows form assignment puzzle slide - c#

I am trying to set the array for all 9 buttons to move around to the blank tile.
for example
this: to this
1 2 3 1 2 3
4 5 6 4 5 6
7 8 7 8
The teacher claims she, "Don’t forget that you will need to cast sender to a Button" this is so I can be able to swap the tiles with the blank tile as shown above. How do I do that?
Then when I reset everything back to its original location in the reset event handler, "I get Object reference not set to an instance of an object." Do you know how I fix that error? This is because it says the same syntax error when I try to cast sender to a Button.
Here is the following code that I hope you can fix.
public partial class frmSlide : Form
{
/*
* frmSlide -constructor
*
*/
Button[,] arr = 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)
{
Button b;
b = new Button();
b.Location = new Point(79, 104);
b.Width = 40;
b.Height = 40;
b.Text = "";
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);
}
private void InitTwoDim(Button[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
Button b;
b = new Button();
b.Location = new Point(79, 104);
b.Width = 40;
b.Height = 40;
b.Text = "1";
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);
arr[i, j] = b;
}
}
}
private void allNumberButton_Click(object sender, EventArgs e)
// this is when I tried to cast button to sender to swap text but nothing happened
{
Button btn8 = (Button)sender;
Button btnBlank = (Button)sender;
string tmp;
tmp = btn8.Text;
btn8.Text = btnBlank.Text;
btnBlank.Text = tmp;
}
/*
* 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)
{
//This is where I tried to reset the button at its original location but got an error about'
//Object reference not set to an instance of an object.'
arr[0, 0].Text = "1";
arr[0, 1].Text = "2";
arr[0, 2].Text = "3";
arr[0, 3].Text = "4";
arr[1, 1].Text = "5";
arr[1, 2].Text = "6";
arr[1, 3].Text = "7";
arr[2, 1].Text = "8";
arr[2, 2].Text = " ";
}
/*
* btn1_Click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btn1_Click(object sender, EventArgs e)
{
}
/*
* btn8_click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btn8_Click(object sender, EventArgs e)
{
btn8.Text = btnBlank.Text;
btnBlank.Text = btn8.Text;
}
/*
* 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)
{
}
}
Can you help me try to swap the text with the blank tile and reset all tiles back to its location? This is because im basically trying to create a slide puzzle using a two dimensional array for the 9 buttons being arranged with the blank button. This is for c#.

If you run your application in Debug (F5) (rather, with an attached debugger), you will generally see and be prompted on which line in your source is giving you the exception. (i.e. uses debug symbols to essentially map back to your source code)
The line arr[0,0].Text = 1 (and every other reference to arr[<column>, <row>] usage in that scope) is going to throw an object reference exception if there are no instances of Button in arr.
Note that the line Button[,] arr = new Button[3, 3]; dimension the array (arr) by defining the length and type of that 2 Dimension array of type Button, but it doesn't actually create\construct button instances.
So, to resolve this object reference exception, you need to ensure those button instances exist before attempting to use the .Text property.
You can do that by calling to InitTwoDim and passing arr in your form load event handler: InitTwoDim(this.arr); But, this in this.arr is unnecessary code, and passing the argument is also unnecessary.
The following code will refactor that method call, and move it to the class constructor.
public frmSlide()
{
InitializeComponent();
InitializeSliderButtons();
}
void InitializeSliderButtons() {
int i = 1;
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++, i++) {
arr[c, r] = GetNewButton(i.ToString(), c * defaultButtonWidth, r * defaultButtonHeight);
this.Controls.Add(arr[c, r]);
}
}
}
int defaultButtonWidth = 40;
int defaultButtonHeight = 40;
Button GetNewButton(string btnText, int offsetX, int offsetY) {
var b = new Button()
{
Location = new Point(79 + offsetX, 104 + offsetY),
Width = defaultButtonWidth,
Height = defaultButtonHeight,
Text = btnText,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(FontFamily.GenericSerif, (float)15.0)
};
b.Click += new System.EventHandler(this.allNumberButton_Click);
return b;
}
Noteworthy
The click event handler is assumed to be the same for all buttons here.
a 2D array is also 0 based indexed array. Currently, your reset function is attempting to index with 3 (like arr[0,3]) that will cause a different exception (out of range).
These Button instances should be added to the Form and their Button.Location assign, or Point instance, should be handled; I'm defining and including offsetX and offsetY as arguments on the helper to ensure the buttons do not stack at the same location.
I defined defaultButtonWidth and defaultButtonHeight for maintainability. They should be defined with static readonly keywords (you should do that); but, I didn't want to introduce concepts you would have to explain on.

Related

C#/Visual Studio trying to change the content of random array and getting 'Object reference not set to an instance of an object.' error

I am trying to make a tic tac toe game where the user inputs the dimensions and then a board is created with buttons. When a button is clicked, it is disabled and the text inside changes to "X" or "O" accordingly. The user plays against a very basic "AI" which picks the buttons at random.I'm trying to check for empty (enabled) buttons on the board but I get the error:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
heres the code:
public partial class Form1 : Form{
int num;
Button[,] buttonspinak;
private void button2_Click(object sender, EventArgs e)
{
int start = 180, end = 30;
num = Convert.ToInt32(textBox1.Text);
buttonspinak = new Button[num, num];
for (int i = 0; i < num; i++)
{
end += 80;
start = 180;
for (int j = 0; j < num; j++)
{
Button b = new Button();
b.Size = new Size(60, 60);
b.Location = new Point(start, end);
this.Controls.Add(b);
start += 80;
b.BackColor = Color.White;
buttonspinak[i, j] = b;
b.Click += new EventHandler(Computer2);
Computer(sender, e);
}
}
}
int countercomputer;
Random randcompu = new Random();
private void Computer(object sender, EventArgs e)
{
int randomi = 0;
int randomj = 0;
Button b = (Button)sender;
b.Enabled = false;
randomi = randcompu.Next(0, num);
randomj = randcompu.Next(0, num);
**while (buttonspinak[randomi, randomj].Text == "O" || buttonspinak[randomi, randomj].Text == "X")** // this is the line where i get the error
{
randomi = randcompu.Next(0, num);
randomj = randcompu.Next(0, num);
//buttonspinak[randomi, randomj].Text = "C";
}
buttonspinak[randomi, randomj].Text = "O";
b_Elegxos();
}
private void Computer2(object sender, EventArgs e)
{
countercomputer++;
Button b = (Button)sender;
b.Enabled = false;
if (countercomputer % 2 != 0)
{
b.Text = "X";
b.ForeColor = Color.DarkRed;
}
b_Elegxos();
}
}
You seem to call Computer() in each iteration of your nested for loop in the button2_Click handler.
This means that, after only initializing buttonspinak[0,0] with a new Button, Computer() gets called, which picks a random position in buttonspinak and tries to get that element's Text. But, most likely, that position in the array isn't initialized yet, so you're trying to call .Text on a null reference, resulting in your exception.
You should in stead call Computer() after the for loops in button2_Click, so you can be sure all positions in buttonspinak are initialized.

How to dynamically create buttons and to add different event handler?

I created a function that appends buttons in a form. Buttons when clicked supposed to open different files. File paths are in listbox1. I want to add a click event for every button I append. One button should open one file from listbox1.
The part with appending buttons works, but I can't add a different event for each of them only one.
This is my code. It adds the event to every button but only the last one.
PlaySong is a function that plays ".mp3" file. That works.
Can someone help me?
int i = 0;
private void Load_Songs()
{
List<string> url = new List<string>();
url = listBox1.Items.Cast<String>().ToList();
int p = 5;
for (int j = 0; j < listBox1.Items.Count; j++)
{
EventHandler klik = new EventHandler(Playing);
Song_Data titl = new Song_Data(url[j]);
Button n = new Button
{
Text = titl.Title,
Location = new Point(0, p + 20),
Width = ClientRectangle.Width / 3,
FlatStyle = FlatStyle.Flat
};
p += 20;
n.Click += klik;
List_Artist.Controls.Add(n);
i++;
}
}
private void Playing(object sender, EventArgs e)
{
PlaySong(listBox1.Items[i].ToString());
}
You don't need many event handlers, just store the index to the button's Tag in your loop and then use it to find which index you should use to choose from listbox:
Button n = new Button
{
Text = titl.Title,
Location = new Point(0, p + 20),
Width = ClientRectangle.Width / 3,
FlatStyle = FlatStyle.Flat,
Tag = j
};
Then in your handler:
private void Playing(object sender, EventArgs e)
{
int i= (int)((Button)sender).Tag;
PlaySong(listBox1.Items[i].ToString());
}
to use different handler for each button you can use anonymous event handler, but won't solve your problem:
n.Click += (s, ev) =>
{
//code when button clicked
};

Can we use parameter in mouse click event c# [duplicate]

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.

Handle button click event of button list inside UIScrollView Monotouch

I have a UIScrollView and a UIPageControl on my View in my iPhone app. I populate this Scrollview with a list of 10 buttons. The buttons all populate correctly an I can scroll through them perfectly. My question is: How do I wire up a click event to each of those buttons? Each button will perform essentially the same task (Play a sound) however the sound will be different for each of these buttons. The buttons are created programatically by the following method:
private void CreatePanels()
{
scrollView.Scrolled += ScrollViewScrolled;
int count = 10;
RectangleF scrollFrame = scrollView.Frame;
scrollFrame.Width = scrollFrame.Width * count;
scrollView.ContentSize = scrollFrame.Size;
for (int i=0; i<count; i++)
{
float h = 150.0f;
//float w = 50.0f;
float padding = 10.0f;
int n = 25;
var button = UIButton.FromType (UIButtonType.RoundedRect);
button.SetTitle (i.ToString (), UIControlState.Normal);
UIImage img = new UIImage("Images/btntest.png");
button.SetImage(img, UIControlState.Normal);
button.Frame = new RectangleF (
(padding + 40) * (i + 1) + (i * (View.Frame.Width - 100)) ,
padding,
View.Frame.Width - 100,
h);
RectangleF frame = scrollView.Frame;
PointF location = new PointF();
location.X = frame.Width * i;
frame.Location = location;
button.Frame = frame;
scrollView.AddSubview(button);
}
pageControl.Pages = count;
}
private void ScrollViewScrolled (object sender, EventArgs e)
{
double page = Math.Floor(((scrollView.ContentOffset.X - scrollView.Frame.Width) / 2) / scrollView.Frame.Width) + 1;
pageControl.CurrentPage = (int)page;
}
The CreatePanels() method is called within the ViewDidLoad() method which populates the UIScrollView.
How can I link a click event to each of these buttons? I have searched the internet a lot but to no avail.
What about wiring up an anonymous method to each click event in the for loop?
button.TouchUpInside += (s, e) =>
{
//play i.mp3
};
I'm not sure how to actually play a sound file but you could have your sound files named as i.* since that is what your buttons are named.

EventHandlers and the sender

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.

Categories