Handling an array of buttons - c#

I'm creating a simple game where a melody is played, and buttons that corresponds to notes are supposed to be highlighted.
Then the user is to push the button, and during each click the buttons are to highlighted again. I would like to place all the buttons in the GUI graphically.
Can I add a highLight method to the buttons in the GUI? I know I probably could create a new class that ineherits from some button class and create the buttons in the code but I would prefer to do it graphically.
What is the neatest way to handle the button outputs? I know I could paste in code for each button like
private void button_withIndexA(object sender, EventArgs e)
{
checkIfThisNoteWasCorrect();
highLightThisButton();
setHighLightOffForAllOtherButtons();
}
However, I think it would be neater to collect all buttons in some sort of container class and make a function like
class buttonArrayHandler
{
/*constructors etc*/
private void someButtonWasClicked(object sender)
{
/*Check which button was clicked, and do stuff accordingly*/
}
}
However I don't know how to do that. Suggestions?

You could change the button colors to create a highlight effect, but if it's a game you can use images / graphics for buttons, and swap them to another graphic when clicked.
When you double click on a button / graphic / control, by default it creates a method and links it to the click action for you. Instead click on each control, then the little lightning icon, and under the click action, pick the same method for all of them.
Then in your method cast the object sender to get the original control, for example:
var clickedButton = (Button)sender;
Where (Button) may be (Graphics) or whatever type of control you used as a button.
EDIT:
If you need to access a group of controls, you can either keep a global list of names at the top of the form and loop through them:
public List<string> buttonList = new List<string>() { "button1", "button2" };
void SomeMethod()
{
foreach (var controlName in buttonList)
{
this.Controls[controlName].Text = "TEST";
}
}
Or use a fixed name and number range:
void SomeMethod()
{
for (int i = 1; i <= 2; i++)
{
this.Controls["button" + i].Text = "TEST";
}
}

I would recommend adding the buttons programatically just like Carlos487 mentioned, here is the code segment that I made and could work to your advantage:
public Form1()
{
InitializeComponent();
int topMod = 0;
for (int i = 0; i < 5; i++)
{
MakeButton(i,topMod);
topMod += 20;
}
}
public void MakeButton(int index, int margin)
{
Button currentButton = new Button();
currentButton.Text = "Note" + index;
currentButton.Top += margin;
currentButton.Click += OnButtonClick;
panel1.Controls.Add(currentButton);
}
public void OnButtonClick(object sender, EventArgs e)
{
//checkIfThisNoteWasCorrect();
//highLightThisButton();
//setHighLightOffForAllOtherButtons();
MessageBox.Show("My Action was activated!");
}
As you can see you can place the common functions within the OnButtonClick method like I did here, giving all of the buttons the same event sequence. If you do not want to go through the whole process of programming buttons then you could also just do this:
currentButton.Click += OnButtonClick;

Related

Where to start generating buttons on WPF form

I have WPF form with Grid and inside that Grid in Row(1).Column(1) i have StackPanel.
Inside that StackPanel i want to generate buttons.
I don't know how many buttons will be generated, since form(with grid and stackPanel) can be of different size.
Code below works, buttons are getting generated if i run that piece of code on Button_Click for example.
But buttons are not generated if I run this piece of code after InitializeComponent().
I guess, that after InitializeComponent WPF form is still not drawn(or finished loading) so my stPanel.ActualHeigh =="0", and since I can't divide with zero nothing acctualy happens.
Can you suggest some workaround, or even better proper solution?
public partial class frmReceipt : Window
{
public frmReceipt()
{
InitializeComponent();
addButtonGrp(); //am i too fast :)
}
private void addButtonGrp()
{
//Calculate size of container to determine numbers of button
int btnMinimumHeightSize = 30;
int btnNumberCreated = (Convert.ToInt16(stPanel.ActualHeight) / btnMinimumHeightSize);
for (int i = 0; i < btnNumberCreated; i++)
{
CreateGroupButtons btn = new CreateGroupButtons();
var btnX = new Button();
btnX = (btn.addButton(i, btnMinimumHeightSize, Convert.ToInt16(stPanel.ActualWidth)));
btnX.Click += ClickHandlerGrp;
if (i == btnNumberCreated - 1)
{
btnX.Height = btnMinimumHeightSize + ((Convert.ToDouble(stPanel.ActualHeight) / btnMinimumHeightSize) % 1) * (btnNumberCreated);
}
stPanel.Children.Add(btnX);
}
}
private void ClickHandlerGrp(object sender, RoutedEventArgs e)
{
var button = sender as Button;
MessageBox.Show("Clicked button number: " + button.Tag);
string test = Convert.ToString(button.Tag);
switch (test)
{
case "PLUGrp":
addButtonGrp(); //this is just for test, i don't want to generate buttons this way
break;
default:
break;
}
}
}
}
Thanks a lot!
I think you're right about running your code before the form has displayed. (It would be easy to check by putting a breakpoint on the for loop)
You can use the Loaded event of the form. Put this in your XAML for the window
Loaded="MainWindowView_OnLoaded"
and this in your C#
private void MainWindowView_OnLoaded(object sender, RoutedEventArgs e)
{
addButtonGrp();
}
This should then fire after the form is displayed, when you know the height of your stack panel.

Change background colour of button after multiple buttons are clicked

I am using Visual C# 2010
I have a windows form with about 24 buttons plus a 'Finish' button
Currently the Finish button is red. I want the colour of this button to change to green once all other buttons have been clicked.
When the user clicks button 1 -24 that button changes to green, so I know how to change the background on a click event.
I have a boolean for when each button is clicked.
This is the code I have so far:
if (button1Clicked && button2Clicked ...) // I have button3 through button24 as well
{
finishButton.BackColor = Color.Green;
}
However the button remains Red when all 24 buttons have been clicked.
I have sent the output of button1 to the debug writeline and I know that the boolean changes from false to true when the button is clicked.
I have made the finish button public in the modifiers option.
Am I missing a step? I thought this should have been easy to accomplish.
EDIT
public partial class Form2 : Form
{
public bool button1Clicked = false;
.... // and other buttons are allocated the same way
}
private void button1_Click(object sender, EventArgs e)
{
String textForClipboard = String.Format("{0}_S1_{1}", Form1.dateValue, button1.Text);
Clipboard.SetText(textForClipboard);
button1.BackColor = Color.Green;
button1Clicked = true;
System.Diagnostics.Debug.WriteLine(button1Clicked);
}
EDIT
Where exactly should I have the if loop?
I currently have it in this method
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
....
public Form2(Form parentForm)
{
InitializeComponent();
mainForm = parentform;
....
if (button1Clicked)
{
finishedButton.BackColor = Color.Green;
}
}
}
}
This is a quick'n'dirty solution, which however works. It assumes that your 24 buttons are named (not their text) button1 to button24.
1) put your 24 buttons in a GroupBox (e.g. groupBox1)
2) put this in partial class Form2 : Form
bool[] allButtons = new bool[24];
private void button_Click(object sender, EventArgs e)
{
var indexButton = int.Parse((((Button)sender).Name.Substring(6))) - 1;
allButtons[indexButton] = true;
allButtonsClicked();
}
private void allButtonsClicked()
{
const int totalButtons = 23;
int counter = 0;
for (int i = 0; i < totalButtons; i++)
{
if (allButtons[i])
{
counter++;
}
}
if (counter == totalButtons)
{
finishButton.BackColor = Color.Green;
}
}
3) put this in Form2_Load()
foreach(Control ctl in groupBox1.Controls)
{
if (ctl is Button)
{
ctl.Click += button_Click;
}
}
What it does:
It loops through all the controls in groupBox1 when Form2 loads, checks if they are buttons and hooks all 24 buttons click events to button_click(). This way you don't need to declare 24 button clicks, since button_click() will trigger every time each of the 24 buttons is clicked.
We then have an allButtons bool array with 24 positions, all initially set to false. Each time button_click() triggers, we extract the "numerical" portion of the name of the button triggering button_click() at that given time (so for example if it's button1 triggering button_click() at than given time, we take substring "1" from "button1"), convert that to an int (so "1" becomes 1) and use that to set allButtons[0] = true (the array is zero-based, hence why 1 is used for index 0). When all 24 elements of allButtons become true (which is checked in allButtonsClicked()), we change finishButton's color to Color.Green.
A small modification to globetrotter's answer.
Change the allButtonsClicked() to return bool instead of void:
private bool allButtonsClicked()
{
const int totalButtons = 23;
int counter = 0;
for (int i = 0; i < totalButtons; i++)
{
if (allButtons[i])
{
counter++;
}
}
if (counter == totalButtons)
{
return true;
}
return false;
}
So you can then do:
private void button_Click(object sender, EventArgs e)
{
var indexButton = int.Parse((((Button)sender).Name.Substring(6))) - 1;
allButtons[indexButton] = true;
if (allButtonsClicked())
{
finishButton.BackColor = Color.Green;
}
}
This just results in the code being more readable and understandable in every-day language: "Whenever a button is clicked, check if each of the 24 buttons has been clicked at least once. If yes, then change the BackColor of finishButton to Green" and centralises your logic in one place rather than two.
First of all, be sure that the inner part of the condition is executed anytime.
for example you can pop up a message box.
Check only for 3 buttons for first, smaller test cases.
For other test, remove the whole condition and use only the color changing. Is this works?
If not, then you have to play around that part for first.
If it is works, then go back to your condition, that will be the leaker. Check for first only one button clicking. And then two etc.
In that way you can easily find your answer anytime. Do it in small tasks, to find the issue.
Tick a debug point to the condition, and check which flag is false. Mouse over button1clicked etc, all should be true

How to use "for" to set the background image of a group of buttons in a C# Windows Forms application

I want to use a wheel to change (for example) 30 buttons' background image each time a form loads.
I cannot use this:
for(int i=1;i<=30;i++)
{
button i .backgroundimage=image.fromfile("URL");
}
What should I do?
There are many possible interpretations of your problem. Why can't you use your code? There are also different solutions for your problem.
As example:
public Form1() // Constructor
{
InitializeComponent(); // Ensure all controls are created.
List<Button> buttons = new List<Button>(30);
buttons.Add(mybutton1)
buttons.Add(mybutton2)
// Go futher with all your buttons.
}
private void Form1_Load(object sender, System.EventArgs e) // Create a load event
{
foreach(Button button in buttons)
{
button.BackgroundImage = Image.FromFile(path);
// Note: The file remains locked until the Image is disposed!
}
}
Well you could use something like this assuming this code executes in a Form_Load and the buttons Parent control is your form. Have in mind that you should supply the real path to your image that you want to set as a background image
string path = "rootNameOfTheImage";
int counter = 0;
foreach(Control ctrl in this.Controls)
{
if(ctrl is Button)
{
Button btn = (Button)ctrl;
if(/* test if this button should be used */)
{
btn.BackgroundImage=Image.FromFile(path + counter++.ToString() + ".jpg");
}
}
}

Why does my Tooltip not display its tip?

I have a Tooltip with the ShowAlways property set to true.
On the controls where I want the tooltip to display (LinkLabels in this instance), I see there is a "ToolTip on <name of my Tooltip>" property which expects a string.
However, my tooltip is shared between 5 LinkLabels, and should differ depending on which one is hovered over.
I do have a shared click event that works:
private void linkLabelPlatypus1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
int Platypus = 1;
LinkLabel ll = null;
if (sender is LinkLabel)
{
ll = sender as LinkLabel;
}
if (null != ll)
{
if (ll.Name == linkLabelPlatypus2.Name)
{
Platypus = 2;
} else if (ll.Name == linkLabelPlatypus3.Name)
{
Platypus = 3;
} else if (ll.Name == linkLabelPlatypus4.Name)
{
Platypus = 4;
} else if (ll.Name == linkLabelPlatypus5.Name)
{
Platypus = 5;
}
toolTipPlatypi.SetToolTip(ll, DuckbillData.GetPlatypusDataForToolTip(Platypus));
}
}
...but I want the tooltips to also show on hover, and not require the user to click the label.
You only need to set the tooltip once :
public Form1()
{
InitializeComponent();
toolTip1.SetToolTip(linkLabel1, "foo");
toolTip1.SetToolTip(linkLabel2, "bar");
}
Done.
Doing this in a MouseHover or MouseEnter handler will call this function over and over each time the event fires. It will work, but it is unnecessarily complicated.
You only need one ToolTip on a form to provide tips for any number of components and it can provide them all simultaneously and continuously (ie: you don't have to change it or set it each time). Each component can have only one tip, but you can change it throughout the program any time you like. ShowAlways does not have to be true - it is used to make tooltips show on forms which are not active (ie: hover over an inactive window behind one with focus, etc).
You should write a event handler for Mouse Hover and have your tool tip display logic inside it.
private void Label1_MouseHover(object sender, System.EventArgs e)
{
//display logic
}
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.mousehover.aspx

Visual Studio 2010 TableLayoutPanel with Buttons events

I've made in Visual Studio 2010 in designer mode a TableLayoutPanel (for example 8x8 rowsxcolumns), then I've put in each field of this "matrix" a button. Is there any way how to handle the events centrally? I found that TableLayoutPanel have TabIndexChanged, but I don't know if this will be useful for me.
I'm just trying to find out if is possible this:
I click on second button and get the information on what button I've clicked and will be able to change some of his property - for example icon, or text.
Lets imagine an idea that I will understand tablelayoutpanel as an "matrix" or "array" and I want to work with it something like this:
1)I'll click on button on possition i (or i,j)
2)I'll change the Icon of button on possition i (or i,j)
Is that possible? If yes, how?
Thx
You can have a single method handle all your button click events and then use the sender parameter to modify the image on the button.
e.g.
private void CreateButtons(object sender, EventArgs e)
{
for (int i = 0; 8 < length; i++)
{
for (int j = 0; 8 < length; i++)
{
var btn = new Button() { Text = "Something" };
btn.Click += new EventHandler(btn_Click);
this.tableLayoutPanel1.Controls.Add(btn, i, j);
}
}
}
void btn_Click(object sender, EventArgs e)
{
((Button)sender).Image= SomeMethodThatReturnsAnImage();
}
As an aside the TabIndexChanged would be completely useless to you since its only raised when the TabIndex property of a control gets changed which is typically very rare. You'll rember that the TabIndex property is used to control the order of controls for when the user presses the Tab key.
Sounds like you just need to associate the click event of all your buttons with a single event handler, and then manipulate the clicked button, etc., inside the event handler

Categories

Resources