How to remove items from a list dynamically? - c#

I'm making a small project to answer questions. I have 2 questions:
How do I remove the answers dynamically?
How can I move the answers up and down i.e: reorganise them?
It will look something like this and when I click on the button ( which says "A..." on the image) it will delete the Radio button and text-box respectively.
Each item(radio-button, text-box, button) is in a list and I was thinking that a click on the button will remove the items from the lists, those that are in same position/index.
The way I add those items is like this (it might help).
if (contar < 10)
{
contar++;
radioButtons.Add(new MetroFramework.Controls.MetroRadioButton());
btnRmv.Add(new MetroFramework.Controls.MetroButton());
for (int i = 0; i < radioButtons.Count; i++)
{
Textboxes.Add(new MetroFramework.Controls.MetroTextBox());
Textboxes[i].Height = 20;
Textboxes[i].Width = 580;
Textboxes[i].Anchor = (System.Windows.Forms.AnchorStyles.Top);
Textboxes[i].Location = new System.Drawing.Point(txtPergunta.Location.X - 25, txtPergunta.Location.Y+txtPergunta.Height+ i * 25);
PanelCentro.Controls.Add(Textboxes[i]);
BtnAdd.Location = new System.Drawing.Point(BtnAdd.Location.X, Textboxes[i].Location.Y + 25);
radioButtons[i].Anchor = (System.Windows.Forms.AnchorStyles.Top);
radioButtons[i].Location = new System.Drawing.Point(Textboxes[i].Location.X - 20, Textboxes[i].Location.Y);
PanelCentro.Controls.Add(radioButtons[i]);
btnRmv[i].Height = 20;
btnRmv[i].Width = 20;
btnRmv[i].Anchor = (System.Windows.Forms.AnchorStyles.Top);
btnRmv[i].Text = "delete";
btnRmv[i].Location = new System.Drawing.Point(Textboxes[i].Location.X + Textboxes[i].Width + 20, Textboxes[i].Location.Y);
PanelCentro.Controls.Add(btnRmv[i]);
btnRmv[i].Click += RemoverResposta;
}
}
I was using a for loop to delete the items but it never deleted the correct items (i.e: the items in the same position of the button I clicked), it deleted all.

Related

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();

getting all values of datagridview in loop

i am trying to enter record in database from datagridview filled by textfields so i am using loop to get all records one by one and save it but now problem is that i am unable to get datagridview row[num] and it giving me error "index must be not null or negative value".
BSL.invoiceDetail idBSL = new BSL.invoiceDetail();
//MessageBox.Show("proID from datagridview = " + (dataGridView1.Rows[1].Cells[1].Value).ToString());
for (int i = 0; i <= dataGridView1.RowCount; i++)
{
idBSL.invoiceId = 1;
idBSL.proId = Convert.ToInt32(dataGridView1.Rows[i].Cells[0].Value);
idBSL.quantity = Convert.ToInt32(dataGridView1.Rows[i].Cells[2].Value);
idBSL.unitPrice = Convert.ToInt32(dataGridView1.Rows[i].Cells[3].Value);
idBSL.netProfit = Convert.ToInt32(dataGridView1.Rows[i].Cells[4].Value);
}
Two minor corrections in your code.
DataGridView is 0 based index, so loop exit condition should be
i < dataGridView1.RowCount
Loop not generating the object collection for each row, it is
updating same instance again and again (idBSL)
You have to make both mentioned corrections to make code work.
Alternatively, you can also consider/use below Linq approach.
var invoiceCollection = dataGridView1.AsEnumerable().Select((row, index) => new {
invoiceId = index+1,
proId = Convert.ToInt32(row.Cells[0].Value),
quantity = Convert.ToInt32(row.Cells[2].Value),
unitPrice = Convert.ToInt32(row.Cells[3].Value),
netProfit = Convert.ToInt32(row.Cells[4].Value)
}).ToList()

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);
}

Radio button set needs a selection limit in custom control

public HtmlGenericControl fieldset = new HtmlGenericControl("fieldset");
public HtmlGenericControl legend = new HtmlGenericControl("legend");
String[] options = ListString.Split(',');
for (int i = 0; i < options.Length; i++)
{
RadioButton aRadioButton = new RadioButton();
aRadioButton.Text = options[i];
fieldset.Controls.Add(aRadioButton);
}
fieldset.Controls.add(legend)
Yet when the radio list appears on my page, multiple radio buttons are selectable. How do I restrict the selection limit to 1?
Try this:
aRadioButton.GroupName = "myGroup";
Each RadioButton must be in a group so that it knows when to deselect the others from the same group.

Categories