Printing data from dynamically added combo boxes? - c#

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.

Related

Adding groups and items to ListView in C# windows form

I'm trying to create a ListView in Windows Form that contains groups and items i get from DataBase.
My ListView is called "lstItems"
In the begining, the ListView is empty and I fill it with data during the runnig of the program.
This is the code I use to create the groups:
foreach(DataRow r in tasksTbl.Rows)
{
string groupName = "group" + num;
num++;
lstItems.Groups.Add(groupName, r.Field<string>(0));
}
The tasksTbl table is not empty and it creates several group that I cannot see on the screen at this point.
This is the code I use to create the items and subItems for the groups:
private void CreateItem(DataTable tbl)
{
int taskId = tbl.Rows[0].Field<int>(0);
string taskName = tbl.Rows[0].Field<string>(1);
DateTime startDate = tbl.Rows[0].Field<DateTime>(2);
DateTime endDate = tbl.Rows[0].Field<DateTime>(3);
string dateStr = startDate.ToString() + " - " + endDate.ToString();
ListViewItem item = new ListViewItem(dateStr);
item.Tag = taskId.ToString();
foreach (DataRow r in tbl.Rows)
{
string position = r.Field<string>(5);
string soldier = r.Field<string>(6);
item.SubItems.Add(soldier + " (" + position + ")");
}
foreach(ListViewGroup grp in lstItems.Groups)
if (grp.Header.Equals(taskName))
grp.Items.Add(item);
}
Here also the tbl table is not empty and it creates the items and sub items to each group.
I can see in the debugger that the groups has the items properly.
My problem is that I cannot see the groups or the items on the screen.
What am I missing?
Can someone give me a hand?
Thank you in advance!
I figured out my problem.
I needed to add columns to the ListView and then, to add the items to the ListView and only in the end to add the items to the groups.
I did it and now it works.
itzick,
You need to create the groups as you go and assign them to the items you add to the ListView Control.
Here is a simple example which loads a ListView with the numbers 65 to 76. The groups are based upon the number modulus 5.
Create a form, add a ListView called listView1, add the method below and call that method during form load. You should see a ListView with five groups and a few member items in each group.
private void LoadListView()
{
// Assume we are in a form, with a ListView control called listView1 on the form
// Create a group label array
var groupLabels = new string[5];
groupLabels[0] = "aaa";
groupLabels[1] = "bbb";
groupLabels[2] = "ccc";
groupLabels[3] = "ddd";
groupLabels[4] = "eee";
for (var i = 65; i < 76; i++)
{
// Find group or create a new group
ListViewGroup lvg = null;
var found = false;
foreach (var grp in listView1.Groups.Cast<ListViewGroup>().Where(grp => grp.ToString() == groupLabels[i % 5]))
{
found = true;
lvg = grp;
break;
}
if (!found)
{
// Group not found, create
lvg = new ListViewGroup(groupLabels[i % 5]);
listView1.Groups.Add(lvg);
}
// Add ListViewItem
listView1.Items.Add(new ListViewItem {Text = i.ToString(CultureInfo.InvariantCulture), Group = lvg});
}

Copy multiple selected rows from datagridview to textboxes

I have a datagridview named PTable which shows a table from database. I also have a button that functions to copy the data from selected rows to the textboxes I have.
This code that I have right now copies two selected rows from the datagridview but when I only select one row or when I select more than 2 rows, it says that "Index was out of range"
What should happen is that it should copy any number of rows and not only 2 rows
private void button11_Click(object sender, EventArgs e)
{
productid.Text = PTable.SelectedRows[0].Cells[0].Value + string.Empty;
productname.Text = PTable.SelectedRows[0].Cells[1].Value + string.Empty;
unitprice.Text = PTable.SelectedRows[0].Cells[4].Value + string.Empty;
productid2.Text = PTable.SelectedRows[1].Cells[0].Value + string.Empty;
productname2.Text = PTable.SelectedRows[1].Cells[1].Value + string.Empty;
unitprice2.Text = PTable.SelectedRows[1].Cells[4].Value + string.Empty;
}
If a user have not selected two rows, your index 1 (PTable.SelectedRows[1]) is not valid, because the item is not there.
Before you can run this code, you have to check, if the user selected two rows:
if (PTable.SelectedRows.Count == 2)
{
//Your code ...
}
else
{
//Not two rows selected
}
First, make sure SelectionMode of DataGridView to FullRowSelect(Hope you have done it already)
Second, Use foreach or any looping logic to go through each selected rows.
Third, It's always better to write modular code. Write a validation method which returns TRUE or FALSE based on selection of the grid rows.
Now based on returned value you need to continue writing your business logic.
Fourth, Make sure to use NULL checks
So let's start by re-factoring the code.
private void button11_Click(object sender, EventArgs e)
{
If(IsRowOrCellSelected())
{
//loop through selected rows and pick your values.
}
}
I am just writing sample, make sure PTable is accessible.
private boolean IsRowOrCellSelected()
{
if (PTable.SelectedRows.Count > 0)
{
DataGridViewRow currentRow = PTable.SelectedRows[0];
if (currentRow.Cells.Count > 0)
{
bool rowIsEmpty = true;
foreach(DataGridViewCell cell in currentRow.Cells)
{
if(cell.Value != null)
{
rowIsEmpty = false;
break;
}
}
}
if(rowIsEmpty)
return false;
else
return true;
}
Code can be still improved.
To work with varying number of selected rows I suggest the following.
My approach is:
There may be more than 5 rows, that You have to display. First create 3 panels for the TextBoxes. I have named the one for the productids as here. Generate the textboxes from code, this way it's easier to maintain more than 5 selected rows:
// This code goes to the constructor of the class. Not in the button click
List<TextBox> productids = new List<TextBox>();
List<TextBox> productnames = new List<TextBox>();
List<TextBox> unitprices = new List<TextBox>();
for (int i = 0; i < 5; i++)
{
productids.Add(new TextBox { Top = i * 32 });
productnames.Add(new TextBox { Top = i * 32 });
unitprices.Add(new TextBox { Top = i * 32 });
here.Controls.Add(productids[i]);
here2.Controls.Add(productnames[i]);
here3.Controls.Add(unitprices[i]);
}
Than You can in the button click set the value for each selected row:
// This code goes to the button click
// first empty the Textboxes:
foreach (TextBox productid in productids)
{
productid.Text = string.Empty;
}
foreach (TextBox productname in productnames)
{
productname.Text = string.Empty;
}
foreach (TextBox unitprice in unitprices)
{
unitprice.Text = string.Empty;
}
for (int i = 0; i < PTable.SelectedRows.Count; i++)
{
productids[i].Text = PTable.SelectedRows[i].Cells[0].Value.ToString();
productnames[i].Text = PTable.SelectedRows[i].Cells[1].Value.ToString();
// or better... Name the columns
// unitprices[i].Text = PTable.SelectedRows[i].Cells["unitPrices"].Value.ToString();
unitprices[i].Text = PTable.SelectedRows[i].Cells[4].Value.ToString();
}

Multiple combo boxes added to the FlowLayoutPanel dynamically, if I onchange of one combo box remaining also affected

I added multiple combo boxes to the FlowLayoutPanel dynamically. If I receive an onchange on one, the combo boxes remaining are also affected. I don't want to affect the remaining combo boxes. What should I do?
foreach(var video in videos)
{
var subvideos = video.Descendants("subvideos");
if (subvideos.Count() >= 1)
{
ComboBox subvideo = new ComboBox();
subvideo.Name = "subvideo" + i;
subvideo.Items.Add(video.Attribute("name").Value);
foreach(var videoname in subvideos)
{
subvideo.Items.Add(videoname.Value);
}
flowlayoutpanel1.Controls.Add(subvideo);
i++;
}
else
{
var text = new TextBox();
text.Text = video.Attribute("name").Value;
flowlayoutpanel1.Controls.Add(text);
}
}

Flow Layout Panel Population 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);
}

Access a DataGridView via a parent (TabControl) to set value -- C# form

This question is related to one I asked earlier:
Adding buttons to a TabControl Tab in C#
In short: I have programmatically added controls to tabs in a c# form.
I would now like to access the tab's controls (in this case a DataGridView) and set some values.
this.dataGridView1.Rows[r].Cells[1].Value = "General";
Above is how I have done it before, but I can't use this right now due to scope, so I need to access the DataGridView via the parent:
// THIS IS NON WORKING CODE NO COMMENTS ABOUT THE SYNTAX PLEASE
languageTabs.TabPages[0].Controls["grid"].Row[int].Cells[int].Value = "General";
// TabControl -> First Tab -> DataGridView -> the column -> row -> set value
Is there a way to do the same functionality of the first code snippet, but using the parents like in the second snippet?
EDIT:
Here is some more code if it helps:
while ((line = stringReader.ReadLine()) != null)
{
//if the line isn't a comment (comments start with an '/')
if (line[0] != '/')
{
// split the string at each tab
string[] split = line.Split(new char[] { '\t' });
// is this line the "blah"?
if (split[0] == "blah")
{
// we now need to set up the tables to be used
for (int i = 1; i < split.Length; i++)
{
// add a tab for each language
string tabTitle = split[i];
newTab = new TabPage(tabTitle);
newTab.Name = tabTitle;
languageTabs.TabPages.Add(newTab);
// add a DataGridView to each tab
grid = new DataGridView();
grid.SetBounds(14, 68, 964, 420);
grid.Name = tabTitle + "Grid";
// set the columns in the DataGridView
stringIdColumn = new DataGridViewTextBoxColumn();
stringIdColumn.HeaderText = "String ID";
stringIdColumn.Width = 75;
stringIdColumn.Name = tabTitle + "StringIDColumn";
grid.Columns.Insert(0, stringIdColumn);
...
...
// add the DataGridView and button to each tab
languageTabs.TabPages[split[i]].Controls.Add(grid);
}
}
else
{
// this isn't the identifier it must be the start of the languages
// load the strings to the tables
// THIS IS WHERE I WANT MY CODE
}
This is how I got it working:
Control[] ctrls = languageTabs.TabPages[1].Controls.Find("EnglishGrid", true);
if (ctrls[0] != null)
{
DataGridView dgv = ctrls[0] as DataGridView;
dgv.Rows[r].Cells[0].Value = 1;
}

Categories