Flow Layout Panel Population C# - c#

I'm trying to populate a Flow Layout Panel with ComboBoxes and NumbericUpDowns.
The problem I'm having is using both the new NumbericUpDowns with the new ComboBoxes. Here is how I'm generating the ComboBoxes and NumericUpDowns.
// This int increments each time the code is run. It's located outside of the method below.
int captchaID = 0;
.
// Textboxes that are only for the UI, no code interaction based on text input.
string textboxText = "captchaTextbox";
TextBox newTextbox = new TextBox();
newTextbox.Name = captchaID.ToString() + textboxText;
newTextbox.Text = "";
newTextbox.Width = 175;
itemFlowPanel.Controls.Add(newTextbox);
// Combo Boxes
string comboBoxText = "captchaComboBox";
ComboBox newComboBox = new ComboBox();
newComboBox.Name = captchaID.ToString() + comboBoxText;
newComboBox.Width = 50;
newComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
itemFlowPanel.Controls.Add(newComboBox);
// This array holds my strings that are added to each ComboBox
string[] skills = new string[6];
skills[0] = "STR";
skills[1] = "DEX";
skills[2] = "CON";
skills[3] = "INT";
skills[4] = "WIS";
skills[5] = "CHA";
// This for loop is just populating my ComboBox with the array.
for (int i = 0; i < skills.Length; i++)
{
newComboBox.Items.Add(skills[i]);
}
// Numeric Up Downs
string numericUpDownText = "captchaNumericUpDown";
NumericUpDown newNumericUpDown = new NumericUpDown();
newNumericUpDown.Name = captchaID.ToString() + numericUpDownText;
newNumericUpDown.Width = 50;
newNumericUpDown.ValueChanged += new EventHandler(captchaNumericUpDown_Click);
newNumericUpDown.ValueChanged += new EventHandler(captchaNumericUpDown_ValueChanged);
itemFlowPanel.Controls.Add(newNumericUpDown);
captchaID++;
With the current code, I'm able to edit an EventHandler that each NumericUpDown contains, but I haven't found a way to make it able to read it's corresponding combobox (which increment together with captchaID).
What I'd like to be able to do, is create a new unique event for each, but if that's not possible, a way to check the ID of the combobox would help as well.

Here are quick solutions:
1) By using dictionary
Dictionary<NumericUpDown, ComboBox> _controls = new Dictionary<NumericUpDown, ComboBox>();
// when you create comboBox - add entry with associated numericUpDown
_controls.Add(numericUpDown1, comboBox1);
// now in the numericUpDown event you can get combobox like this
void numericUpDown_Whatever(object sender, WhateverEventArgs e)
{
var numericUpDown = (NumericUpDown)sender;
var comboBox = _controls[numericUpDown];
// do something
var selectedIndex = comboBox.SelectedIndex;
...
}
2) By using Tag
// add combobox into numericUpDown Tag when you create them
numericUpDown1.Tag = comboBox1;
// now in the numericUpDown event you can get combobox like this
void numericUpDown_Whatever(object sender, WhateverEventArgs e)
{
var numericUpDown = (NumericUpDown)sender;
var comboBox = (CombBox)numericUpDown.Tag;
// do something
var selectedIndex = comboBox.SelectedIndex;
...
}

You can rewrite your captchaNumericUpDown_ events to take a ComboBox as an additional parameter and then call them like this:
newNumericUpDown.ValueChanged += (sender, args) =>
{
captchaNumericUpDown_Click(sender, args, newComboBox);
}

Related

WPF put XAML refrences into a c# list

Right now I have 15 items, that I have to set the value to one by one, is there a way I can put all the refrences into one c# list or something, so I could loop through it?
//TITLES
BTitle0.Text = Items[14]["title"];
BTitle1.Text = Items[13]["title"];
BTitle2.Text = Items[12]["title"];
BTitle3.Text = Items[11]["title"];
BTitle4.Text = Items[10]["title"];
BTitle5.Text = Items[9]["title"];
BTitle6.Text = Items[8]["title"];
BTitle7.Text = Items[7]["title"];
BTitle8.Text = Items[6]["title"];
BTitle9.Text = Items[5]["title"];
BTitle10.Text = Items[4]["title"];
BTitle11.Text = Items[3]["title"];
BTitle12.Text = Items[2]["title"];
BTitle13.Text = Items[1]["title"];
BTitle14.Text = Items[0]["title"];
Basically I want to have a list to XAML refrences like BTitle[0].text..., Is there any way to do that?
Try the FindName method of the window or control that hosts the TextBox elements:
for (int i = 0; i < 15; i++)
{
TextBox textBox = FindName("BTitle" + i) as TextBox;
if (textBox != null)
{
textBox.Text = "...";
}
}
Or use an ItemsControl that binds to the items.

Changing order of controls in flow layout panel during runtime

I have a Windows form on which I put a flow layout panel. I also have a class that reads a local database and returns the corresponding values. Depending on the user input via a button(s) the panel gets filled with other buttons. The amount of these buttons depends on the values in the local database. The buttons are displayed correctly and with the correct information, but the order in which they are displayed is alphabetical even though the data-table, from the database class, is ordered in the correct way (via the numerical value of the "ID" column from the database).
I have also added a data-grid view to check and there the items are displayed in the correct way. I have tried to add a for-each loop but that just seems to randomize the order of the buttons.
Does anybody know how I can get the buttons to be displayed in the correct way, so that the button with the lowest "ID" value gets displayed first.
Here is the code for displaying the buttons:
//set the datagridview with the correct values/names. Order works perfectly
dataGridView_AttackName.DataSource = db.attackIDName(attackCategory, taughtOn);
DataTable dt = db.attackIDName(attackCategory, taughtOn);
//sort datable again because doesnt work from db class
dt.DefaultView.Sort = "ID";
dt.DefaultView.ToTable();
int horizontal = 0;
int vertical = 0;
Button[] buttonArray = new Button[dt.Rows.Count];
for (int items = 0; items < buttonArray.Length; items++)
{
buttonArray[items] = new Button();
buttonArray[items].Size = new Size(150, 50);
buttonArray[items].Location = new Point(horizontal, vertical);
buttonArray[items].Name = string.Format("Button_{0}", dt.Rows[items]["ID"].ToString());
buttonArray[items].Text = dt.Rows[items]["Name"].ToString();
buttonArray[items].Click += btn_msg;
if ((items + 1) < buttonArray.Length)
{
vertical += 50;
}
flowLayoutPanel_AttackName.Controls.Add(buttonArray[items]);
}
//get the correct ID value from the button name and try to order it that way
foreach (Button b in flowLayoutPanel_AttackName.Controls)
{
string name = b.Name;
string subname = name.Substring(name.IndexOf("_") + 1);
int i = Convert.ToInt32(subname);
flowLayoutPanel_AttackName.Controls.SetChildIndex(b, i);
}
I have searched around on this site but couldn't find anything that worked.
You've correctly sorted the DefaultView by the ID, but then haven't used the results!
You need to replace, for example, dt.Rows[items]["Name"]with dt.DefaultView[items].Row["Name"]. You then don't need the statement dt.DefaultView.ToTable().
Full code is below:
dt.DefaultView.Sort = "ID";
int horizontal = 0;
int vertical = 0;
Button[] buttonArray = new Button[dt.Rows.Count];
for (int items = 0; items < buttonArray.Length; items++)
{
buttonArray[items] = new Button();
buttonArray[items].Size = new Size(150, 50);
buttonArray[items].Location = new Point(horizontal, vertical);
buttonArray[items].Name = string.Format("Button_{0}", dt.DefaultView[items].Row["ID"].ToString());
buttonArray[items].Text = dt.DefaultView[items].Row["Name"].ToString();
buttonArray[items].Click += btn_msg;
if ((items + 1) < buttonArray.Length)
{
vertical += 50;
}
flowLayoutPanel_AttackName.Controls.Add(buttonArray[items]);
}

TextBox Text doesn't update

I create TextBoxes dynamically, one for each column in a table(DataGridView). Every time I select a row in Table a text Box should have the value of the primaryKey row. First time it works, but after that the TextBox Text is not changing. But if I put a MessageBox to print the textbox.Text it printed it right, but not displayed it. When I tried the same thing with textboxes that aren't created dynamically I didn't have this problem.
for (int i = 0; i < columns_number; i++)
{
textBoxes[i] = new TextBox();
labels[i] = new Label();
labels[i].Text = childTable.Columns[i].Name;
labels[i].Location = new Point(x_point - 100, y_point);
textBoxes[i].Location = new Point(x_point, y_point);
this.Controls.Add(labels[i]);
this.Controls.Add(textBoxes[i]);
y_point = y_point + 30;
}
textBoxes[number].Text = selectedRow.Cells[vaterPrimaryKey].Value.ToString();
textBoxes[number].Refresh();
Use the below code to find the textboxs and use them the way you want :
string textboxName = "textboxes" + number.ToString
TextBox mytextbox = this.Controls.Find(textboxName, true).FirstOrDefault() as TextBox;
mytextbox.Text = selectedRow.Cells[vaterPrimaryKey].Value.ToString();

Combobox SelectedItem not working propely

I'm trying to retrieve the selected item from a combobox, though i can't get it to work.
Form1 form = new Form1();
string cpuCount = form.comboBox1.SelectedItem.ToString();
Now, this is not returning anything. BUT, if i insert this code in my InitializeComponent(), it selects item with index = 3, and return that proper item.
comboBox1.SelectedIndex = 3;
Why does it behave like this? If I now select for example item with index = 5, it still will think the selected item is the one with index = 3.
---------- I think i should expand to show you how my code looks.
Form1 - adding all items to the comboboxes.
public partial class Form1 : Form
{
Profile profile = new Profile();
public Form1()
{
InitializeComponent();
Profile profile = new Profile();
string[] prof = profile.getProfiles();
foreach (var item in prof)
{
comboBox5.Items.Add(Path.GetFileNameWithoutExtension(item));
}
int ram = 1024;
for (int i = 0; i < 7; i++)
{
comboBox4.Items.Add(ram + " GB");
ram = ram * 2;
}
int vram = 512;
string size;
for (int i = 0; i < 5; i++)
{
if(vram > 1000)
{
size = " GB";
}
else
{
size = " MB";
}
comboBox2.Items.Add(vram + size);
vram = vram * 2;
}
for (int i = 1; i < 5; i++)
{
comboBox1.Items.Add(i * 2);
}
for (int i = 0; i < 5; i++)
{
comboBox3.Items.Add(i * 2);
}
private void button3_Click(object sender, EventArgs e)
{
string current = profile.currentProfile();
profile.saveProfile(current);
}
}
So, button3 is my "save"button.
And here is my "Profile"-class
class Profile
{
public string folder { get; set; }
public Profile()
{
this.folder = "Profiles";
if (!File.Exists(folder))
{
Directory.CreateDirectory(folder);
File.Create(folder + "/default.cfg").Close();
}
}
public string[] getProfiles()
{
string[] files = Directory.GetFiles(folder);
return files;
}
public void saveProfile(string filename)
{
Form1 form = new Form1();
string cpuCount = "cpuCount=" + form.comboBox1.SelectedItem;
string RAM = "maxRAM=" + form.comboBox4.SelectedItem;
string VRAM = "maxVRAM=" + form.comboBox2.SelectedItem;
string threads = "cpuThreads=" + form.comboBox3.SelectedItem;
string path = folder + "/" + filename;
StreamWriter sw = new StreamWriter(path);
string[] lines = { cpuCount, RAM, VRAM, threads };
foreach (var item in lines)
{
sw.WriteLine(item);
}
sw.Close();
}
public string currentProfile()
{
Form1 form = new Form1();
string selected = form.comboBox5.SelectedValue + ".cfg".ToString();
return selected;
}
}
Thank you.
The problem is that there is nothing selected in your ComboBox. You create your form and then, without previous user interaction, you want to get the SelectedItem which is null at that moment.
When you create ComboBox control and fill it with items, SelectedItem property is null until you either programratically set it (by using for example comboBox1.SelectedIndex = 3) or by user interaction with the control. In this case you are not doing anything of the above and that is why you are geting the mentioned error.
EDIT Based on the edited question
Change your code like this:
first change the saveProfile method so you could pass the four strings which you write into the text file. Note that you could alternatively pass the reference of the form but I wouldn't suggest you that. So change the method like this:
public void saveProfile(string filename, string cpuCount, string RAM , string VRAM , string threads)
{
string path = folder + "/" + filename;
using(StreamWriter sw = new StreamWriter(path))
{
sw.WriteLine("cpuCount=" + cpuCount);
sw.WriteLine("maxRAM=" + RAM );
sw.WriteLine("maxVRAM=" + VRAM );
sw.WriteLine("cpuThreads=" + threads);
}
}
And then call it from button3 Click event handler like this:
private void button3_Click(object sender, EventArgs e)
{
string current = profile.currentProfile();
string cpuCount = this.comboBox1.SelectedItem.ToString();
string RAM = this.comboBox4.SelectedItem.ToString();
string VRAM = this.comboBox2.SelectedItem.ToString();
string threads = this.comboBox3.SelectedItem().ToString();
profile.saveProfile(current, cpuCount, RAM, VRAM, threads);
}
Or alternatively
private void button3_Click(object sender, EventArgs e)
{
string current = profile.currentProfile();
profile.saveProfile(current, this.comboBox1.SelectedItem.ToString(), this.comboBox4.SelectedItem.ToString(), this.comboBox2.SelectedItem.ToString(), this.comboBox3.SelectedItem().ToString());
}
From what I can see, you are calling form.comboBox1.SelectedItem.ToString() right after the creation of Form1. This means that the cpuCount variable is initialized right after the form is created, thus far before you have the chance to change the selected item with your mouse.
If you want to retrieve the value of the combobox after it is changed, you can use the SelectedIndexChanged event.
First of all, add a Form_Load Event and put your code in the handler. (use constructor for property initialization and other variable initialization)
private void Form1_Load(object sender, EventArgs e)
{
this.comboBox1.SelectedItem= 5; // This will set the combo box to index 5
string cpuCount = this.comboBox1.SelectedText; // This will get the text of the selected item
}
so you get the value of item at index 5 in cpuCount variable.
The selected clause gives you the values AFTER you have selected something, by default(when you run your app) there is nothing selected in the comoboBox, hence, it displays the value as null, after selecting the item you can use the combobox's selectedItem, selectedIndex, selectedText and selectedValue properties.
You can also use databinding to display items in the combobox, which in my view is a better way then adding the items manually.
to databind your combobox you can use,
// Bind your combobox to a datasource, datasource can be a from a database table, List, Dataset, etc..
IDictionary<int, string> comboDictionary = new Dictionary<int, string>();
comboDictionary.Add(1, "first");
comboDictionary.Add(2, "second");
comboDictionary.Add(3, "third");
comboBox1.DataSource = comboDictionary;
comboBox1.DisplayMember = "Key";
comboBox1.ValueMember = "Value";
//
And here now you can use combobox1.SelectedIndex to go through the item collection in the datasource :) and it will give you the value against your keys when you use combobox1.SelectedValue. Hope this helps.
Your ComoboBox doesn't have items in it. So it will not return properly. You are accessing combobox selected value rights after making form object.
And if it comboBox has items then nothing is being selected. By default nothing is selected in comboBox. You need to set it. Use this. What it returns? Set comboBox.SelectedIndex and then get selectedItem.
int selectedIndex = form.comboBox1.SelectedIndex;
Try this. Add some items in ComboBox and then get selectedItem.
Form1 form = new Form1();
form.comboBox1.Add("Item 1");
form.comboBox1.Add("Item 2");
form.comboBox1.Add("Item 3");
form.comboBox1.SelectedIndex = 1;
string cpuCount = form.comboBox1.SelectedItem.ToString();

Printing data from dynamically added combo boxes?

I have gotten the code to work so that when I press the button, "Add More", it adds more combo boxes to the form with the names dayBox1, dayBox2, and so on.
This is the code for that:
private void addMoreBtn_Click(object sender, EventArgs e)
{
//Keep track of how many dayBoxes we have
howMany++;
//Make a new instance of ComboBox
ComboBox dayBox = new System.Windows.Forms.ComboBox();
//Make a new instance of Point to set the location
dayBox.Location = new System.Drawing.Point(Left, Top);
dayBox.Left = 13;
dayBox.Top = 75 + dayLastTop;
//Set the name of the box to dayBoxWhateverNumberBoxItIs
dayBox.Name = "dayBox" + howMany.ToString();
//The TabIndex = the number of the box
dayBox.TabIndex = howMany;
//Make it visible
dayBox.Visible = true;
//Set the default text
dayBox.Text = "Pick One";
//Copy the items of the original dayBox to the new dayBox
for (int i = 0; i < dayBoxO.Items.Count; i++)
{
dayBox.Items.Add(dayBoxO.Items[i]);
}
//Add the control to the form
this.Controls.Add(dayBox);
//The location of the last box's top with padding
dayLastTop = dayBox.Top - 49;
}
What is the best way to print the selected member of the boxes added with the button event?
The way I was printing the information I wanted to a file before was like this (from only one box):
public void saveToFile()
{
FileInfo t = new FileInfo("Workout Log.txt");
StreamWriter Tex = t.CreateText();
Tex.WriteLine("---------------Workout Buddy Log---------------");
Tex.WriteLine("------------------- " + DateTime.Now.ToShortDateString() + " ------------------");
Tex.Write(Tex.NewLine);
if (dayBoxO.Text != "Pick One")
{
Tex.WriteLine("Day: " + dayBoxO.Text);
}
else
{
Tex.WriteLine("Day: N/A");
}
}
I want to be able to do this for every box, each on a new line. So, it would be like:
Day: (the text of box1)
Day: (the text of box2)
Day: (the text of box3)
and so on...
Thanks!
Two ways I see immediately that you could do this:
The first is to maintain a list of references to those controls when you are adding them.
private List<ComboBox> _comboBoxList = new List<ComboBox>();
...
//Make a new instance of ComboBox
ComboBox dayBox = new System.Windows.Forms.ComboBox();
//Add your combobox to the master list
_comboBoxList.Add(dayBox);
...
Then your saveToFile() method would contain a line that looked something like this:
foreach(var box in _comboBoxList)
{
Tex.Write(Tex.NewLine);
if (box.Text != "Pick One")
{
Tex.WriteLine("Day: " + box.Text);
}
else
{
Tex.WriteLine("Day: N/A");
}
}
The second method is to do as Mike pointed out and walk the controls list looking for your ComboBoxes.
Assuming the controls live on the form, walk through all the controls on the form:
var boxes = from Control c in this.Controls
where c.GetType() == typeof(System.Windows.Forms.ComboBox)
select c;
StringBuilder sb = new StringBuilder();
foreach (Control c in boxes)
{
sb.AppendLine(c.Text); // ...
}
Non-Linq approach:
StringBuilder sb = new StringBuilder();
foreach (Control c in this.Controls)
{
if (c.GetType() == typeof(System.Windows.Forms.ComboBox))
{
sb.AppendLine(c.Text); // ...
}
}
It's a little hacky to rely on the Controls collection, however; especially when you start to have panels and other groupings. You might consider adding the controls to your own collection, as Josh suggests.

Categories