I have this lines of code:
button0.BorderBrush = new SolidColorBrush(Colors.Red);
button1.BorderBrush = new SolidColorBrush(Colors.Red);
button2.BorderBrush = new SolidColorBrush(Colors.Red);
...
How can I make correct this:
(button + "numberOfButton").BorderBrush = new SolidColorBrush(Colors.Red);
Any time you find yourself with variables like this:
button0
button1
button2
etc...
What you should have is an array. If the controls themselves are already static on the form, then you can simply build the array when loading the form. Something like this:
public class MyForm : Form
{
private IEnumerable<Button> myButtons;
public MyForm()
{
myButtons = new List<Button>
{
button0, button1, button2 // etc...
};
}
// etc...
}
Then when you need to loop over the buttons, you simply loop over the collection:
foreach (var button in myButtons)
button.BorderBrush = new SolidColorBrush(Colors.Red);
If you need to reference the collection elements by index, use an IList<> instead of an IEnumerable<>. If you need to do more complex things, use any number of collection types.
You can find it by name with method Control.Find:
var button = this.Control.Find("button0", true).FirstOrDefault();
But better to store buttons in array and get them by index:
var buttons = new Control[10];
buttons[0] = button0;
...
Related
Premise:-
We have a list of key value pairs.
The Item count of this list will vary.
We have a form with a bunch of default buttons on it. (Edit - Built earlier in the designer, not at runtime.)
The buttons are named "button1, button2, ..."
We have more buttons than items in the list.
At runtime we want to transfer information from the list elements to the buttons and hide the unused buttons.
My question is how to address those buttons from withing a loop?
Using a for loop in VBA I could say this:-
Me.Controls("TB_Item" & Format(i, "00")).Visible = False
In C# I have this minimal example as a starting point (The form has 10 buttons):-
public UF_ButtonLoop()
{
InitializeComponent();
List<KeyValuePair<string, string>> MyItems = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Apple", "Green Fruit"),
new KeyValuePair<string, string>("Orange", "Orange Fruit"),
new KeyValuePair<string, string>("Sprout", "Spawn of the Devil"),
new KeyValuePair<string, string>("Hershey Bar", "A bit like chocolate"),
new KeyValuePair<string, string>("Beefburger", "Man Food")
};
//Loop through the 10 buttons
for (int i = 1; i < 11; i++)
{
if (i <= MyItems.Count )
{
//Transfer Data from list to button
//Pseudo code
Control("Button" + i).Text = (MyItems.ElementAt(i).Key);
Control("Button" + i).Tag = (MyItems.ElementAt(i).Value);
}
else
{
//Hide the button as we've reached the end of the list so have no use for it.
//Pseudo code
Control("button" + 1).Hide();
}
// Note, VBA methos is:-
// Me.Controls("TB_Item" & Format(i, "00")).Visible = False
}
}
Control("Button" + i) is not correct syntax.
Can I do this in C#, if so how?
If not what is the correct way?
Also I'm new here so if I'm asking things in the wrong way please don't be shy in telling me so!
Many thanks,
Owen S.
Firstly thanks for the help, it's appreciated, especially the nudge towards doing it via other methods, or even different controls.
The answer to the question is probably split into two options:-
(Answer 1) - Do it properly by creating just what you need at runtime, rather than using the static designer and then hiding what was over-built.
(Answer 2) - Quick "bodge" to get the code in my initial question working. (My C# experience can be measured in hours so I have plenty to learn but also need to get code working today for work...)
So one bodge method that works is to put the buttons in their own list and then reference that. Like so:-
public UF_ButtonLoop()
{
InitializeComponent();
//TransferListToButtone_Rev1();
List<KeyValuePair<string, string>> MyItems = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Apple", "Green Fruit"),
new KeyValuePair<string, string>("Orange", "Orange Fruit"),
new KeyValuePair<string, string>("Sprout", "Spawn of the Devil"),
new KeyValuePair<string, string>("Hershey Bar", "A bit like chocolate"),
new KeyValuePair<string, string>("Beefburger", "Man Food")
};
List<Control> ListOfButtons = new List<Control>
{
button1, button2, button3, button4, button5, button6, button7, button8, button9, button10
};
void A_Button_Click(object sender, System.EventArgs e)
{
Console.WriteLine((sender as Button).Text + " = " + (sender as Button).Tag);
}
//Loop through the 10 buttons
for (int i = 0; i < 10; i++)
{
if (i < MyItems.Count )
{
//Transfer Data from list to button
ListOfButtons.ElementAt(i).Text = (MyItems.ElementAt(i).Key);
ListOfButtons.ElementAt(i).Tag = (MyItems.ElementAt(i).Value);
//Set Click Event
ListOfButtons.ElementAt(i).Click += new EventHandler(A_Button_Click);
}
else
{
//Hide the button as we've reached the end of the list so have no use for it.
ListOfButtons.ElementAt(i).Hide();
}
}
}
Cheers all,
Owen S.
I would recommend to create those buttons when you read the list. Do not create the buttons statically in designer. You need to create a List of buttons in the main Form and then add the buttons programmatically.
List creation
List<Button> buttons = new List<Button>();
Then in some loop where you go thru your data list
Button MyButton = new Button();
Mybutton.Location = new Point(YourX, YourY);
Mybutton.Text = "AnyText";
Mybutton.AutoSize = false;
MyButton.Size = new Size(width, height);
Mybutton.BackColor = Color.LightBlue;
Mybutton.Padding = new Padding(6);
buttons.add(MyButton);
Ad1.
The best practice is to create your own "SpecialButton" class which is Inheritted from original Button class. Then you can add some special attributes to the button as is reference on some other element which should the SpecialButton control and etc...
How do i create a new Button/Canvas with a dynamic name?
Button {buttonname read from text file} = new Button;
I have googled this for a while now but i can't find the solution.
Thank you!
I'm not sure if I understood correctly, but that name in your example is not the button name, it's just the reference name used in code to access the button. The button name would be set like this:
buttonRefName.Name = "ButtonName1";
So you can set the name to whatever you want: dynamically generated names inside a loop, names read from a file, etc...
You can use the same reference name for multiple buttons, just be sure to add it to List or to WPF Window, Panel, etc... before creating the new one:
var buttonList = new List<Button>();
var buttonRef = new Button { Name = "YourButtonName" };
buttonList.Add(buttonRef);
buttonRef = new Button { Name = "YourButtonName2" };
buttonList.Add(buttonRef);
It not possible the way you want to do it. If you are reading from a text file better use a List or better a Dictionary... an example use is as follows:
var buttons = new Dictionary<string, Button>();
buttons["yourName"] = new Button();
// logic goes here
I have a series of lists in a static class (used as a global class)
public static class globalClass
{
public static List<classA> aList = new List<classA>();
public static List<classB> bList = new List<classB>();
public static List<classC> cList = new List<classC>();
}
I want to generate a xaml button for each list, and was told reflection was a bad idea. This is how I handled it using reflection.
//get FieldInfo for globalClass
TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(typeof(globalClass));
IEnumerable<FieldInfo> FieldInfoList = typeInfo.DeclaredFields;
foreach (FieldInfo f in FieldInfoList)
{
//Only look at lists
if(f.FieldType.ToString().StartsWith("System.Collections.Generic.List`1")){
StackPanel s = new StackPanel();
s.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.FontSize = 45;
textBlock.Text = f.Name.ToString();
Button addButton = new Button();
addButton.Click += delegate(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(addObjectToLibraryPage), f);
};
addButton.Margin = new Thickness(10);
addButton.Name = "addButton";
addButton.Content = "add";
Button deleteButton = new Button();
deleteButton.Click += delegate(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(deleteObjectFromLibraryPage), f);
};
deleteButton.Margin = new Thickness(10);
deleteButton.Name = "deleteButton";
deleteButton.Content = "delete";
s.Children.Add(addButton);
s.Children.Add(deleteButton);
//add new textBlock and stackpanel to existing xaml
stackPanel.Items.Add(textBlock);
stackPanel.Items.Add(s);
}
}
Is there any cleaner way to do this? Hopefully I would like to be able to pass the actual list instead of a FieldInfo.
I don't want to have to handle each list individually because I may end up with 20+ lists and am using them all in a very similar way.
An example of what I am trying to do:
Suppose I have a grocery/nutrition App, and I want users to be able to record what they eat/need from the store. They can select from a list of Fruit, Vegetables, Meat, Dairy, Sweets, Canned Goods, Etc..
But, I want them to be able to (as an advanced option) be able to edit the list of possible fruits, or any other food category. And I don't want to just have a list of "food" because meat will record things like minimum cooking temperature or something like that.
So, under advanced options, I would want two buttons for each category (add to fruit, delete from fruit). And theoretically add an Import/Export page so I can share my list of fruits with other people or something.
It doesn't seem like the answers pointing to using a superclass will work. See: C# polymorphism simple question
You can create a list to contain all the existing lists you have. You can then iterate over the list to create the buttons. If you wish to maintain a label for each list you could use a dictionary with the key as the label text and list as the value.
The proposed solution aside, do take into account the comments given from Sayse.
I am creating an application where a user will input grades and the program will output the weighted average. On load, it will ask for the number of categories for the assignments. The program will then dynamically create textboxes for the user to input information. The problem is that I can not figure out how to read the text that is inputed after I create the textboxes. Here is my code:
TextBox txtbx = new TextBox();
txtbx.Text = "";
txtbx.Name = "txtbx1";
txtbx.Location = new Point(10, 10);
txtbx.Height = 20;
txtbx.Width = 50;
Controls.Add(txtbx);
How can I change this code so I can find the current text in the box when the user submits?
If you are dynamically generating controls then obviously you won't be able to have a field for each one. But if you are trying to access the Controls collection for a named control, the ControlCollection can be indexed by name. After adding the text box with the specified name, you can simply do:
TextBox txtbx = (TextBox)Controls["txtbx1"];
You could use the FindControl method of the Page class.
This method takes a parameter which is the TextBox's ID, which you have to set upon creation:
txtbx.ID = "txtbx1";
Then you can select it:
TextBox txtbx1 = (TextBox)FindControl("txtbx1");
and use it.
Edit: Since the initial question added that he is refering to Windows Forms, my reply above is off-topic.
In Windows Forms, you should simply use a class member variable instead of a local variable. E.g.:
public partial class MyForm
{
...
private TextBox txtbx;
...
private void createControls()
{
txtbx = new TextBox();
txtbx.Text = "";
txtbx.Name = "txtbx1";
txtbx.Location = new Point(10, 10);
txtbx.Height = 20;
txtbx.Width = 50;
Controls.Add(txtbx);
}
private void someOtherFunction()
{
// Do something other with the created text box.
txtbx.Text = "abc";
}
}
This code for the Dynamically Add Textbox On Button Click
int count = 1;
public System.Windows.Forms.TextBox AddNewTextBox()
{
System.Windows.Forms.TextBox txt = new System.Windows.Forms.TextBox();
this.Controls.Add(txt);
txt.Top = count * 25;
txt.Left = 100;
txt.Text = "TextBox " + this.count.ToString();
count = count + 1;
return txt;
}
private void Onbutton_Click(object sender, EventArgs e)
{
//Call the method AddNewTextBox that uses for Dynamically create Textbox
AddNewTextBox();
}
I hope this code will help you .
Thank You
Happy Coding:)
Keep a list of references of all text boxes on the form. Add the textBox reference to the list when you create them dynamically.
Then you can simply iterate through all text boxes in the list when you want to read their text.
Make sure that you name the text boxes as per their related category names. Then you can also Find the control in the list by their names.
class MyForm : Form
{
IList<TextBox> _textBoxes = new List<TextBox>();
private void AddTextBox(string categoryName){
var myTextBox = new TextBox();
myTextBox .Name = categoryName + "txtbx";
// set other properties and add to Form.Controls collection
_textBoxes.Add(myTextBox);
}
private TextBox FindTextBox(string categoryName)
{
return _textBoxes.Where( t => t.Name.StartsWith(categoryName)).FirstOrDefault();
}
}
All you need to do is set up an OnClick listener for your submit button and have it do something like this
private void OnSubmit(object sender, EventArgs args)
{
string yourText = txtbx.Text;
}
You'll have to keep a reference to the text box after you create it. yourText will contain the value you need. Hope this helps
I have a program that I want each person to have their own tab, each tab would be identical, however I would like to remove a tab if I need to.
private void addPerson(string name)
{
TabPage tmp = new TabPage();
ListView tmpList = new ListView();
Button tmpButton = new Button();
this.SuspendLayout();
this.tabFrame.SuspendLayout();
tmp.SuspendLayout();
tmpList.SuspendLayout();
tmpButton.SuspendLayout();
...
//build the controll itself
tmp.Controls.Add(tmpButton);
tmp.Controls.Add(tmpList);
tmp.Location = new System.Drawing.Point(4, 22);
tmp.Name = name.Replace(' ', '_');
tmp.Padding = new System.Windows.Forms.Padding(3);
tmp.Size = new System.Drawing.Size(284, 240);
tmp.TabIndex = 3;
tmp.Text = name;
tmp.UseVisualStyleBackColor = true;
//add it to frame
this.tabFrame.Controls.Add(tmp);
tmpButton.ResumeLayout(true);
tmpList.ResumeLayout(true);
tmp.ResumeLayout(true);
this.tabFrame.ResumeLayout(true);
this.ResumeLayout(true);
{
Name will be in the form "Scott Chamberlain" so I remove the spaces and use underscores for the name field. I can add tabs fine, they show up correctly formated, however when I try to remove the tab using the code:
private void removePerson(string name)
{
this.SuspendLayout();
this.tabFrame.SuspendLayout();
this.tabFrame.Controls.RemoveByKey(name.Replace(' ', '_'));
this.tabFrame.ResumeLayout(true);
this.ResumeLayout(true);
}
The tab does not disappear from my program. What am I missing to remove a tab?
(source: codinghorror.com)
Creating a simple TabPage with a specific Name and adding it to Controls or TabPages works and so does removing it with RemoveByKey on both Controls and TabPages.
Is there any code that might later change the name?
Use tabFrame.TabPages instead of tabFrame.Controls, for both the Add() and RemoveByKey() operations.
TabPages is a more specified version of Controls, and if such a situation occurs you are better of with the more specialized option.