Need to link two button texts to eachother? - c#

I have two table layout panels with 26 buttons each. I need to link the two button texts to each other. I have two Lists containing one language for one set of buttons and another language for another set of buttons. I tried to link the two lists together in order to set their translations but it didnt work. So for example if you click on one button the text "Hello" will show and another button "Salut". If its a match they must either disappear.
Random random = new Random();
List<string> EngBasicPhrases = new List<string>()
{
"Hello", "How are you?", "Hot", "Thank you", "Welcome",
"Let's go", "My name is...", "Cold", "Good luck",
"Congratulations", "Bless you","I forgot","Sorry","I'm fine",
"It's no problem","Don't worry","Here it is","What?","Of course",
"Boy","Girl","Man","Woman","Friend","Almost","Late"
};
List<string> FrBasicPhrases = new List<string>()
{
"Salut","Ca va?","Chaud", "Merci", "Bienvenu", "Allons-y","Je m'appelle","Du froid",
"Bonne chance","Felicitations","A vos souhaits","J'ai oublie","Desole","Je vais bien",
"Ce n'est pas grave","Ne t'en fais pas","Voila","Comment?","Bien sur","Un garcon","Une fille",
"Un home","Une femme","Un ami","Presque","En retard"
};
Button firstClicked, secondClicked;
public Game()
{
InitializeComponent();
AssignWordsToSquares();
EngBasicPhrases.AddRange(FrBasicPhrases);
}
private void Button_Click(object sender, EventArgs e)
{
if (firstClicked != null && secondClicked != null)
return;
Button clickedButton = sender as Button;
if (clickedButton == null)
return;
if (clickedButton.ForeColor == Color.Black)
return;
if(firstClicked == null)
{
firstClicked = clickedButton;
firstClicked.ForeColor = Color.Black;
return;
}
secondClicked = clickedButton;
secondClicked.ForeColor = Color.Black;
CheckForWinner1();
if (firstClicked.Text == secondClicked.Text)
{
firstClicked = null;
secondClicked = null;
}
else
timer1.Start();
}
private void CheckForWinner1()
{
Button button1;
for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
{
button1 = tableLayoutPanel1.Controls[i] as Button;
if (button1 != null && button1.ForeColor == button1.BackColor)
return;
}
MessageBox.Show("Congratulations!");
}
private void Button_Click2(object sender, EventArgs e)
{
if (firstClicked != null && secondClicked != null)
return;
Button clickedButton = sender as Button;
if (clickedButton == null)
return;
if (clickedButton.ForeColor == Color.Black)
return;
if (firstClicked == null)
{
firstClicked = clickedButton;
firstClicked.ForeColor = Color.Black;
return;
}
secondClicked = clickedButton;
secondClicked.ForeColor = Color.Black;
CheckForWinner2();
if (firstClicked.Text == secondClicked.Text)
{
firstClicked = null;
secondClicked = null;
}
else
timer1.Start();
}
private void CheckForWinner2()
{
Button button2;
for (int i = 0; i < tableLayoutPanel2.Controls.Count; i++)
{
button2 = tableLayoutPanel2.Controls[i] as Button;
if (button2 != null && button2.ForeColor == button2.BackColor)
return;
}
MessageBox.Show("Congratulations!");
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
firstClicked.ForeColor = firstClicked.BackColor;
secondClicked.ForeColor = secondClicked.BackColor;
firstClicked = null;
secondClicked = null;
}
private void AssignWordsToSquares()
{
Button button1 = button2;
int randomNumber;
for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
{
if (tableLayoutPanel1.Controls[i] is Button)
button1 = (Button)tableLayoutPanel1.Controls[i];
else
continue;
randomNumber = random.Next(0, EngBasicPhrases.Count);
button1.Text = EngBasicPhrases[randomNumber];
EngBasicPhrases.RemoveAt(randomNumber);
}
for (int i = 0; i < tableLayoutPanel2.Controls.Count; i++)
{
if (tableLayoutPanel2.Controls[i] is Button)
button2 = (Button)tableLayoutPanel2.Controls[i];
else
continue;
randomNumber = random.Next(0, FrBasicPhrases.Count);
button2.Text = FrBasicPhrases[randomNumber];
FrBasicPhrases.RemoveAt(randomNumber);
}
}

Ok, so you seem to have two lists to use for ordinal comparison. So, you don't really need to match the words, but the indexes of both lists. That makes sense. But, then it all gets confusing.
Your code does the following right now:
Create a list of English phrases
Create a list of French phrases
Start form
loop through English panel buttons
add a random English phrase to a button in control panel
remove phrase from English list
loop through French panel buttons
add a random French phrase to a button in control panel
remove phrase from French list
Add all remaining French phrases to the list of remaining English phrases (this step seems unnecessary either way)
So now you have your form loaded. If you have less buttons in your panels than you have words in your list, you might have ended up with words in either panel that cannot be matched.
Now you run into this:
private void Button_Click2(object sender, EventArgs e)
{
[...]
if (firstClicked.Text == secondClicked.Text)
{
firstClicked = null;
secondClicked = null;
}
[...]
this is the only place I see an attempt to compare the values of the button. But, by this point there is no way they'll ever match. "Hello" is a string that will never match "Salut" for example. And, since the lists are no longer intact, they can't be used to help match them. Other wise you could just find phrases in their respective lists and check if indexes match.
To allow this, you need to change how you add the button text in your code. Instead of using the list directly create an array from your list. Something like this:
string[] phrases= EngBasicPhrases.ToArray();
for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
{
if (tableLayoutPanel1.Controls[i] is Button)
button1 = (Button)tableLayoutPanel1.Controls[i];
else
continue;
randomNumber = random.Next(0, phrases.Count - 1);
button1.Text = phrases[randomNumber];
phrases.RemoveAt(randomNumber);
}
phrases= FrBasicPhrases.ToArray();
for (int i = 0; i < tableLayoutPanel2.Controls.Count; i++)
{
if (tableLayoutPanel2.Controls[i] is Button)
button2 = (Button)tableLayoutPanel2.Controls[i];
else
continue;
randomNumber = random.Next(0, phrases.Count);
button2.Text = phrases[randomNumber];
phrases.RemoveAt(randomNumber);
}
There are some other problems in how you're working with the variables and attempting to use colors to determine state of the button, or infer meaning. But, I'll let you work through those. I recommend using breakpoints and stepping through the code to see the state of your variables and intercept events.

I'd use Dictionary. I would link values like this:
button1.Text == dict[button2.Text]

Related

How to force compiler not to consider any block of code if it comes from different button event

In my case, I don't want this block:if (Clicked == true){i = i+2;} and if (Clicked == false){i = i-2;}to work if compiler comes inside of different particular button ;
This is Button2 which returns elements of a sequence for a brand name given as
textBox4.
bool Clicked = false;
private void button2_Click(object sender, EventArgs e)
{
var car = cars.FirstOrDefault(c => c.Brand == textBox4.Text);
i = cars.FindIndex(c => c.Brand == textBox4.Text);
if (car != null)
{
label1.Text = car.Brand;
label2.Text = car.Model;
label3.Text = car.Color;
i++;
}
else
{
MessageBox.Show("This car is not in the list");
}
}
And button3_click event should display next elements of the sequence.
private void button3_Click(object sender, EventArgs e)
{
var car = cars.Select(a => a);
if (Clicked == true)
{
i=i+2;
}
if (i >= 0&&i < cars.Count)
{
label1.Text = car.ToArray()[i].Brand;
label2.Text = car.ToArray()[i].Model;
label3.Text = car.ToArray()[i].Color;
i++; //abc; i = 1;
Clicked = false;
}
}
And Button4 displays the previous elements of a sequence
private void button4_Click(object sender, EventArgs e)
{
if (Clicked == false)
{
i=i-2;
Clicked = true;
}
var car = cars.Select(a => a);
if (i >= 0&&i<=cars.Count)
{
label1.Text = car.ToArray()[i].Brand;
label2.Text = car.ToArray()[i].Model;
label3.Text = car.ToArray()[i].Color;
i--;
}
}
I don't want this :if (Clicked == true){i = i+2;} and this :if (Clicked == false) {i=i-2;} block of codes in the button3 and button4 (next and prev buttons) to run whenever I hit any one of them right after the codes inside of button2 have been worked. I couldn't work it out using bool flags as there're three cases needed to be considered. How to deal with it?
I'm going to try to answer this, not 100% sure exactly what you're asking...
My assumption is that you don't want the "if(Clicked..." part to run if you've just previously clicked button2, but after clicking either button3 or button4, it's fine to run the if statement.
Trying to figure out your code, I think you can shrink it way down to remove those. First, the i++ should be removed from button2_Click
private void button2_Click(object sender, EventArgs e)
{
i = cars.FindIndex(c => c.Brand == textBox4.Text);
if(i >= 0)
{
var car = cars.ToArray()[i]; // no reason to search twice
label1.Text = car.Brand;
label2.Text = car.Model;
label3.Text = car.Color;
}
else
{
MessageBox.Show("This car is not in the list");
}
}
Since the index "i" is not changed in button2_Click, it can be changed in the "next" and "previous" buttons before it's indexed.
private void button3_Click(object sender, EventArgs e)
{
// I don't think you need this
// var car = cars.Select(a => a);
if (i + 1 >= 0 && i + 1 < cars.Count)
{
i++;
var car = cars.ToArray()[i];
label1.Text = car.Brand;
label2.Text = car.Model;
label3.Text = car.Color;
}
}
private void button4_Click(object sender, EventArgs e)
{
if (i - 1 >= 0 && i - 1 <= cars.Count)
{
i--;
var car = cars.ToArray()[i];
label1.Text = car.Brand;
label2.Text = car.Model;
label3.Text = car.Color;
}
}
Since both button3_Click and button4_Click are almost identical, you could probably put them in a single event and check which button is pressed and increment/decrement as needed, but that's an opinion call, there are reasons not to do that as well.
I don't know what type "cars" is, but it may even be better to make a member variable to hold cars.ToArray() so ToArray doesn't have to be called so many times.

Remove TabPage from TabControl

So i need to dynamically add and delete some TabPage. User clicked "Show Tab" = storage_pageadded.
private void storage_menuItem_Click(object sender, EventArgs e) {
storage_page.Text = storage_page.Name = "Storage";
main_tabControl.TabPages.Add(storage_page);
main_tabControl.SelectedTab = storage_page;
}
And the when he chooses another page the storage_page has to be removed
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e) {
for (int i = 0; i < main_tabControl.TabPages.Count; i++) {
if (main_tabControl.TabPages[i].Name.Equals("storage", StringComparison.OrdinalIgnoreCase) && main_tabControl.SelectedTab.Name != "Storage") {
main_tabControl.TabPages.RemoveAt(i);
break;
}
}
}
When i click "Show Tab" page shows up. But when i select other page i see the ArgumentOutOfRangeException leading to Application.Run(new Form_Authentication()); line
How can i do that?
I think the problem is you increase i variable by 1 from 0 to main_tabControl.TabPages.Count
I assume that main_tabControl.TabPages.Count = 10, what is happended if you're removing 7th element? At that time, main_tabControl.TabPages.Count = 4 and i variable = 6. So, i variable is exceed the range of TabPages.
You should to change your code:
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e) {
for (int i = main_tabControl.TabPages.Count - 1; i >=0 ; i--) {
if (main_tabControl.TabPages[i].Name.Equals("storage", StringComparison.OrdinalIgnoreCase) && main_tabControl.SelectedTab.Name != "Storage") {
main_tabControl.TabPages[i].Dispose();
break;
}
}
}

Combining for loop

I am doing a program that has multiple buttons and textboxes. The program I am creating now is not even 50% complete but the code lines is over 5000 making the program too big. Is there a way to combine these for loop statements:
For the first textbox when it is clicked.
var btn = new[] { btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10, btn11, btn12, btn13, btn14 };
for (int i = 0; i < 14; i++)
{
if (txt1.Text == btn[i].Text)
{
txt1.Text = "";
btn[i].Visible = true;
break;
}
}
For the second textbox when it is clicked.
var btn = new[] { btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10, btn11, btn12, btn13, btn14 };
for (int i = 0; i < 14; i++)
{
if (txt2.Text == btn[i].Text)
{
txt2.Text = "";
btn[i].Visible = true;
break;
}
}
and many more textbox.
Take a close look into the code that you are using, The thing which changing based on TextBox is the TextBox itself, so that you can accept it as an argument to the function and wrap the rest of statements inside the function. Then it will check the conditions and Change visibility of the button. which may look like the following:
var arrayButtons = new Button[] { btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10, btn11, btn12, btn13, btn14 };
// Let the array be Global so that we can avoid defining the same every call
public void ChangeButtonVisibility(TextBox currentText)
{
for (int i = 0; i < arrayButtons.Length; i++)
{
if (currentText.Text == arrayButtons[i].Text)
{
currentText.Text = "";
arrayButtons[i].Visible = true;
break;
}
}
}
So that you can call the method like this when TextBox1 Clicked:
ChangeButtonVisibility(TextBox1);
Like this for TextBox2 Clicked
ChangeButtonVisibility(TextBox2);
Or you can make another array with the text boxes and loop over both arrays:
var txts = new[] { txt1, txt2, txt3, txt4, txt5, txt6, txt7, txt8, txt9, txt10, txt11, txt12, txt13, txt14 };
var btns = new[] { btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10, btn11, btn12, btn13, btn14 };
foreach(var txt in txts)
{
foreach(var btn in btns)
{
if (txt.Text == btn.Text)
{
txt.Text = "";
btn.Visible = true;
break;
}
}
}
i would suggest you avoid writing all the specific click events and make one click event for all of your textboxes.
in order to do that you have to add a pointer to the tag of the textbox,which points to corresponding button. i would do that in the load event of the form like this :
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Tag = btn1;
textBox2.Tag = btn2;
textBox3.Tag = btn3;
textBox4.Tag = btn4;
textBox5.Tag = btn5;
textBox6.Tag = btn6;
}
now that you have the buttons as tags of your controls, all you gotta do is make a click event for all your textboxes, cast sender, and check the control versus its tag :
private void allTextboxes_Click(object sender, EventArgs e)
{
if ((sender as TextBox).Text == ((sender as TextBox).Tag as Button).Text)
{
(sender as TextBox).Text = "";
((sender as TextBox).Tag as Button).Visible = true;
}
}
i would strongly prefer this kind of code over loops.

How should I change the visibility of a TextBox when a ComboBox value changes?

I'm new to programming and I'm trying to change the value of a textbox depending of a selected value on a combo box, since the values are numerical 1 to 20, and depending on the selection it will be the number of text boxes visible. I'm using the event selected index changed.
Here is my code:
private void cbxPIN_SelectedIndexChanged(object sender, EventArgs e)
{
int pines = Convert.ToInt32(cbxPIN.SelectedItem.ToString());
if (pines == 1)
{
textbox1.visible = true;
}
else if (pines == 2)
{
textbox1.visible = true;
textbox2.visible = true;
}
...
else if (pines == n)
{
textbox1.visible = true;
textbox2.visible = true;
...
textboxn.visible = true;
}
}
since there are like 25 different numeric values on the combo box is there an easier way of doing this? asides from comparing each different value?
Something like a loop.
At the least, I'd rewrite it like this, to reduce duplication:
private void cbxPIN_SelectedIndexChanged(object sender, EventArgs e)
{
int pines = Convert.ToInt32(cbxPIN.SelectedItem.ToString());
if (pines >= 1)
textbox1.Visible = true;
if (pines >= 2)
textbox2.Visible = true;
...
if (pines >= n)
textboxn.Visible = true;
}
Actually, I'd add all the TextBox controls to a collection, possibly in the constructor:
List<TextBox> TextBoxes = new List<TextBox> { textbox1, textbox2, ... textboxn };
Then use LINQ's Take() method to grab the first xx number of controls and iterate through them:
foreach (var tb in TextBoxes.Take(pines))
textBox.Show();
You want to use a loop structure. You should validate that the number of loops to perform is > 0 and < the number of textboxes you have available, but I'll leave error handling to you.
private void cbxPIN_SelectedIndexChanged(object sender, EventArgs e)
{
int pines = Convert.ToInt32(cbxPIN.SelectedItem.ToString());
TextBox textBox;
for (int i = 1; i <= pines; i++)
{
// get the control from the form's controls collection by the control name
textBox = this.Controls["textbox" + pines.ToString()] As TextBox
textBox.Visible = true;
}
}

Referencing buttons with numbers

Is there a way to reference buttons using a numerical value in C#? I am trying to manipulate the text on buttons using one reusable method. Here is my current coding:
One button click method (there are a total of 16):
private void Card1_Click(object sender, EventArgs e)
{
buff = CardClick(1);
if (buff != null)
{
Card1.Text = buff;
}
}
And the reusable method (the code does have holes, it's in development):
private string CardClick(int card)
{
guesses[g++] = card; //alternate g
if ((guesses[0] != null) && (guesses[1] != null))
{
//Reset Card guesses[0]
//Reset Card guesses[1]
return null;
}
else
{
if (card > 8)
{
return map[2, card];
}
else
{
return map[1, card];
}
}
You can also use Controls.Find() to get a reference to your desired button based on its name:
int i = 1;
Control[] matches = this.Controls.Find("Card" + i.ToString(), true);
if (matches.Length > 0 && matches[0] is Button)
{
Button btn = (Button)matches[0];
// ... do something with "btn" ...
btn.PerformClick();
}
You can use an array of buttons
Button[] buttonArray = new Button[10];
You can get all the buttons from your form by Type and then extract an array:
public Button[] AllButtons()
{
var buttons = new List<Button>();
foreach (var control in this.Controls)
{
if (control.GetType() == typeof(Button))
buttons.Add((Button)control);
}
return buttons.ToArray();
}

Categories

Resources