Changing The Property Of An Object Using Variables? C#/Visual Studio - c#

Ok so I have 100 buttons and I need to change there colors based on conditions in a while loop. They are named button1, button2, button3 ,etc. during the first time around the loop (iteration?) I need to edit button1, the next time button2 the third time button3, etc.
I thought I could just make a string that equaled "button", add the number of times around the loop to it and change the color like that.
String ButtonNumber = "button" + i; where i = number of times around loop
When I try to edit the color using ButtonNumber.BackColor = Color.Red; it won't let me because it's not treating ButtonNumber like a button, but like a string. How do I accomplish this? Thanks! (this is my first time programing pretty much)

Consider using Controls.Find to find a control by name, and then you can change it's properties:
for (int i = 1; i <= 100; i++)
{
var buttonName = string.Format("button{0}", i);
var foundControl = Controls.Find(buttonName, true).FirstOrDefault();
if (foundControl != null)
{
// You can now set any common control property using the found control
foundControl.BackColor = Color.Red;
// If you need to set button-specific properties (i.e. properties
// that are not common to all controls), then cast it to a button:
var buttonControl = foundControl as Button;
if (buttonControl != null)
{
buttonControl.AutoEllipsis = true;
}
}
}

Related

How can I uncheck radiobuttons in C# while still allowing only the first radiobutton to be tabbable?

Not sure if the title makes much sense, so here is the full context:
I'm coding in C#.
I've made an app with several UserControls, each one with many textboxes and radiobuttons.
All radiobuttons are placed in a panel in a set of 2, looking like this:
[ <label> O <radiobutton1text> O <radiobutton2text> ]
(while the first radiobutton have TabStop = true, and the second's TabStop = false)
When tabbing to such panel, only radiobutton1text is focused, and when hitting the LeftArrow key the radiobutton2text is selected. That's the desired outcome.
In order to make a UserControl load faster the second (and above) time, I'm not closing it but rather replacing it with a different UserControl each time the contents need to change.
But this rises an issue: When UserControl X is open, then on top of it I open UserControl Y and then back to X, the textboxes and radiobuttons still have the contents from the first session of when I had UserControl X open for the first time. (I need the contents of textboxes and radiobuttons to be reset after replacing a UserControl).
So I made a function that loops through all controls and empties their contents.
The problem is, when I uncheck the radiobuttons (and restore their TabStop state to true) in this function, the second radiobutton is tabbable after I check either one of them and then invoke the function, whereas it wasn't before going through this function.
The function:
public void BackToMain(object sender, EventArgs e)
{
// Go through all controls and empty each TextBox, RichTextBox, RadioButton or ComboBox.
int parentControlsCount = Controls.Count - 1;
for (int i = parentControlsCount; i >= 0; i--)
{
if (Controls[i].HasChildren == true)
{
int childrenControlsCount = Controls[i].Controls.Count - 1;
for (int j = childrenControlsCount; j >= 0; j--)
{
var controlType = Controls[i].Controls[j].GetType().ToString();
switch (controlType)
{
case "System.Windows.Forms.TextBox":
case "System.Windows.Forms.RichTextBox":
Controls[i].Controls[j].Text = null;
break;
case "System.Windows.Forms.RadioButton":
// Restore both properties to default value
((RadioButton)Controls[i].Controls[j]).Checked = false;
if (j == 1)
((RadioButton)Controls[i].Controls[j]).TabStop = true;
else if (j == 2)
((RadioButton)Controls[i].Controls[j]).TabStop = false;
break;
case "System.Windows.Forms.ComboBox":
((ComboBox)Controls[i].Controls[j]).SelectedIndex = -1;
break;
}
}
}
}
}
What am I doing wrong?
I ended up applying this gross hack- a function on every second radiobutton's CheckedChange:
private void DisableTabStopOnCheckedChange(object sender, EventArgs e)
{
// Assume the following STR:
// 1. In any radiobutton panel, select any radiobutton (without ever invoking BackToMain function in the first post);
// 2. Invoke the BackToMain function;
// 3. In the same radiobutton panel as in step #1, click the second radiobutton.
// Normally, without this function, if the user will now cycle through the controls using the Tab key, both the first and second radiobuttons will be tabbable,
// and that's because in the BackToMain function we reset their Checked and TabStop properies, and that's something that should be handled automatically by the control itself.
// Doing it manually means that for the *first time* selecting the second radiobutton, the first one's TabStop state won't update, which means both radiobuttons
// will have the TabStop state set to true, causing both to be tabbable.
// This is a gross hack to fix this by disabling TabStop on the first radio button if the second one is checked and the first one's TabStop state
// is true (this should happen only after BackToMain has been invoked).
if (((RadioButton)sender).Checked)
{
var firstRadioButton = ((RadioButton)sender).Parent.Controls[1];
if (((RadioButton)firstRadioButton).TabStop == true)
{
((RadioButton)firstRadioButton).TabStop = false;
}
}
}
Not a pretty solution, I know. But it works.

Is it possible to turn a button into a variable?

I have a code in C# with 26 button in it and I want them to all be disabled until the user does something, but I dont want to copy/paste button1.Enable = false; Button2.Enable = false...
So is there a way to do something like this :
for (int i = 1; i < 26; i++)
{
button+i.Enable = false;
}
Thanks for your help.
You probably want to iterate buttons directly.
foreach(var button in this.Controls.OfType<Button>())
{
button.Enable = false;
}
Using either of Controls.Find or Controls.OfType<Button>() only works at run-time. I prefer a statically checkable compile-time approach. Then if you delete a button on the form the code won't compile. Helps give you that type-safe feeling.
I do it this way. Start with a field to hold the buttons in an array:
private Button[] _buttons = null;
Set the array once in your initialization code:
_buttons = new[] { button1, button2, button26 };
Then when you need to do something with the buttons you just do this:
foreach (var button in _buttons)
{
button.Enable = false;
}
You can use Form.Controls.Find()
for (int i = 1; i < 26; i++)
{
this.Controls.Find("button" + i, searchAllChildren: false).Enable = false;
}
The code assumes your buttons are named button1 through button26.
It's not possible directly like you are, but that doesn't make sense anyway.

How to reference a ListBox name using a string in Csharp

for (int i=1; i<4; i++)
{
string buttonName = "button" + i;
if (Controls[buttonName].BackColor = Color.Red)
{
Controls[buttonName].Enabled = false;
}
}
This code works perfectly. The code checks 3 different buttons (button1, button2, button3) and if their color is red they become disabled. The button name is referenced using a string:
Controls[buttonName]
Is there a way to reference a ListBox using a string in the same way? What would "Controls" need to be changed to?
If you simply want to go over all ListBoxes, you could also use .OfType<T>()
foreach (ListBox lb in this.Controls.OfType<ListBox>())
{
lb.Enabled = false;
}
... and it would of course work the same for .OfType<Button>() without the need to name your controls in a way to enumerate them.

Way to format Form with dynamic text fields c#

Right, so I have 13 textboxes with corresponding labels that are assigned after a user decides the name from a different form (instantiated by the 'Add field...' button). The issue arises when the user wishes to delete a textbox with previously entered data, as this results in an empty space where the textbox and label originally were as visualized by the following image:
My question is: how do I make it so that when a user chooses to delete a textbox, the textbox-label pair(s) that follow it replace the deleted textbox AND shift the remaining textboxes accordingly.
Textbox-label pairs in designer:
I've thought about this problem intensively over the past few days, and have concluded that with my current knowledge of C# I am limited to solving this issue with a horrendously tedious amount of if-statements (talking hundreds - thousands here). Any and all help would be appreciated!
Current code on the X-button for first textbox-label pair:
private void xButton1_Click(object sender, EventArgs e)
{
label14.Text = "";
textBox1.Text = "";
if (label14.Text.Equals(""))
{
label14.Visible = false;
textBox1.Visible = false;
xButton.Visible = false;
label14.Text = "";
textBox1.Text = "";
}
if (!textBox2.Text.Equals(""))
{
label14.Text = label15.Text;
textBox1.Text = textBox2.Text;
}
if (!textBox2.Text.Equals("") && (textBox3.Text.Equals("")))
{
label15.Visible = false;
textBox2.Text = "";
textBox2.Visible = false;
xButton2.Visible = false;
}
}
One simple thing you could do is give all your "dynamic" controls (label, textbox, button) a similar value in their Tag property (in my example, I used the string "dynamic" for all the control Tags. This enables you to query for them easily.
Next, you could follow the logic that, anytime you delete some controls, you move all controls below the deleted ones up a distance equal to the height of the control being deleted plus whatever padding you have between the controls.
For example, when a user clicks the X button, since you know the value of the Bottom of the control that's being deleted, you could find all controls that had a matching Tag property whose Top is greater than the x button Bottom, and you can move them up.
Here's an example (this assumes that all your X buttons are mapped to this same click event):
private void buttonX_Click(object sender, EventArgs e)
{
// This is represents the distance between the bottom
// of one control to the top of the next control
// Normally it would be defined globally, and used when you
// lay out your controls.
const int controlPadding = 6;
var xButton = sender as Button;
if (xButton == null) return;
var minTopValue = xButton.Bottom;
var distanceToMoveUp = xButton.Height + controlPadding;
// Find all controls that have the Tag and are at the same height as the button
var controlsToDelete = Controls.Cast<Control>().Where(control =>
control.Tag != null &&
control.Tag.ToString() == "dynamic" &&
control.Top == xButton.Top)
.ToList();
// Delete the controls
controlsToDelete.ForEach(Controls.Remove);
// Get all controls with the same tag that are below the deleted controls
var controlsToMove = Controls.Cast<Control>().Where(control =>
control.Tag != null &&
control.Tag.ToString() == "dynamic" &&
control.Top > minTopValue);
// Move each control up the specified amount
foreach (var controlToMove in controlsToMove)
{
controlToMove.Top -= distanceToMoveUp;
}
}

Centering text on a button in a WinForms application

I have a simple Windows Forms application with a tabControl. I have 3 panels on the tabControl, each having 5 buttons. The text on first set of buttons is hard-coded, but the next set populates when you click one from the first group, and then the same thing happens again for the last group when you click one of the buttons from the second group. In the [Design] view I manually set the TextAlign property of each button to MiddleCenter. However, when I run the application the text on the middle set of buttons is never centered. It is always TopLeft aligned. I've tried changing the font size and even explicitly setting the TextAlign property every time I set button text programmatically, as follows:
private void setButtons(List<string> labels, Button[] buttons)
{
for (int i = 0; i < buttons.Count(); i++)
{
if (i < labels.Count)
{
buttons[i].Text = labels.ElementAt(i);
buttons[i].TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
buttons[i].Enabled = true;
}
else
{
buttons[i].Text = "";
buttons[i].Enabled = false;
}
}
}
This image shows the result:
Does anyone have any ideas for what I'm missing?
Trim text which you are assign to button. Also you can refer label by index, without calling ElementAt
private void setButtons(List<string> labels, Button[] buttons)
{
for (int i = 0; i < buttons.Count(); i++)
{
Button button = buttons[i];
if (i < labels.Count)
{
button.Text = labels[i].Trim(); // trim text here
// button.TextAlign = ContentAlignment.MiddleCenter;
button.Enabled = true;
}
else
{
button.Text = "";
button.Enabled = false;
}
}
}
You can set the UseCompatibleTextRendering property to true, then use the TextAlign property.
The strings in the SQL table that were assigned to the middle column were actually nchar(50), not nvarchar(50), which explains the problem. I added .Trim() to the Text assignment and it looks great now.
You can use the TextAlign from the Properties Menu and set it to MiddleCenter ...
If this does not work then the text you have for your button is larger than the actual button itself... to which you should either rescale your Font Size to a lower base size or a percent size of the actual button by using
btnFunction.Font = new Font(btnFunction.Font.Name, Convert.ToInt32(btnFunction.Height * 0.3333333333333333));
This would cause the button's font to be one third of the height of the button....

Categories