Dynamic Listview with multiple items - c#

So, i have been working in a school project and i need to create and add things to a listview dynamically the 1st adds without any problem, but when it's about to add the 2nd one it crashes.
here is the code:
lvwCarros.Columns.Clear();
ListViewItem lvi = new ListViewItem();
int idx, idx2;
lvwCarros.Columns.Add("Carro", 80, HorizontalAlignment.Center);
lvwCarros.Columns.Add("Ano", 60, HorizontalAlignment.Center);
lvwCarros.Columns.Add("Stand", 60, HorizontalAlignment.Center);
lvwCarros.Columns.Add("Localização do Stand", 120, HorizontalAlignment.Center);
for (idx = 0; idx < Program.Carros.Count ; idx++)
{
lvi.Text = Program.Carros[idx].Marca + " " + Program.Carros[idx].Modelo;
for (idx2 = 0; idx2 < Program.Matriculas.Count ; idx2++)
{
if (Program.Carros[idx].Matricula == Program.Matriculas[idx2].ID_Matricula)
{
lvi.SubItems.Add(Program.Matriculas[idx2].Ano_Emissão.ToString());
for (int idx3 = 0; idx3 < Program.Stands.Count; idx3++)
{
if (Program.Carros[idx].Proprietario == Program.Stands[idx3].ID_Stand)
{
lvi.SubItems.Add(Program.Stands[idx3].Nome_Stand);
lvi.SubItems.Add(Program.Stands[idx3].Local_Stand);
}
lvwCarros.Items.Add(lvi);
}
}
}
}
lvwCarros.Visible = true;
lvwCarros.Enabled = true;
Thank you very much in advance :)

Essentially your problem is that your lvi needs to be a new object every time you call lvwCarros.Items.Add(). Try putting an "lvi = new ListViewItem(first subitem);" in there at the top of that innermost for loop. You can only add each ListViewItem object to the ListView once--this is why you're getting your exception.

Here is the running code. There were two problems in this code:
Each List item to be added should be instantiated in the for loop as it will create a new item. Otherwise, you will be adding the same item again and again and modifying the value of all the items if one item is changed as they will point to same item. Hence move the line ListViewItem lvi = new ListViewItem(); in the for loop if you want to create different list view items.
You are adding the listview item lvi in the internal for loop which is causing it to add the each listitem multiple time. Thats why you are getting the error. Move this line in the outermost for loop and the error will go away.
lvwCarros.Columns.Clear();
int idx, idx2;
lvwCarros.Columns.Add("Carro", 80, HorizontalAlignment.Center);
lvwCarros.Columns.Add("Ano", 60, HorizontalAlignment.Center);
lvwCarros.Columns.Add("Stand", 60, HorizontalAlignment.Center);
lvwCarros.Columns.Add("Localização do Stand", 120, HorizontalAlignment.Center);
for (idx = 0; idx < Program.Carros.Count ; idx++)
{
/* You should initiate a new item in the loop if you want to add more*/
ListViewItem lvi = new ListViewItem();
lvi.Text = Program.Carros[idx].Marca + " " + Program.Carros[idx].Modelo;
for (idx2 = 0; idx2 < Program.Matriculas.Count ; idx2++)
{
if (Program.Carros[idx].Matricula == Program.Matriculas[idx2].ID_Matricula)
{
lvi.SubItems.Add(Program.Matriculas[idx2].Ano_Emissão.ToString());
for (int idx3 = 0; idx3 < Program.Stands.Count; idx3++)
{
if (Program.Carros[idx].Proprietario == Program.Stands[idx3].ID_Stand)
{
lvi.SubItems.Add(Program.Stands[idx3].Nome_Stand);
lvi.SubItems.Add(Program.Stands[idx3].Local_Stand);
}
}
}
}
/*Moving it to outermost for loop*/
lvwCarros.Items.Add(lvi);
}
lvwCarros.Visible = true;
lvwCarros.Enabled = true;

Related

How to add the items to start from the second cell and not in the column header cell?

private void DisplayTableWithListview()
{
listView1.GridLines = true;// Whether the grid lines are displayed
listView1.FullRowSelect = true;// Whether to select the entire line
listView1.View = View.Details;// Set display mode
listView1.Scrollable = true;// Whether to show the scroll bar automatically
listView1.MultiSelect = false;// Is it possible to select multiple lines
// Add header(column)
listView1.Columns.Add("ToString(yyyyMMddHHmm)", 160, HorizontalAlignment.Center);
// Add items into table
for (int i = 0; i < 6; i++)
{
ListViewItem item = new ListViewItem();
item.SubItems.Clear();
item.SubItems[0].Text = "Product" + i.ToString();
item.SubItems.Add(i.ToString());
item.SubItems.Add((i + 7).ToString());
item.SubItems.Add((i * i).ToString());
listView1.Items.Add(item);
}
}
Product0 is in the cell/place of the column header.
i want the items to be start added from the second cell.
you can use listView1.Items.Insert(index , item) to add item in specialty index. listview.listviewitemcollection.insert()
listView1.Items.Insert(2,"yourItem");
if you want the items to be start added from the second cell try this:
listView1.Items.Add("");
for (int i = 0; i < 6; i++)
{
ListViewItem item = new ListViewItem();
item.SubItems.Clear();
item.SubItems[0].Text = "Product" + i.ToString();
item.SubItems.Add(i.ToString());
item.SubItems.Add((i + 7).ToString());
item.SubItems.Add((i * i).ToString());
listView1.Items.Add(item);
}

How to remove unwanted whitespace when filling listview with items (c#)

Using c#, winforms.
Background:
The user should be able to select items from my menu strip, and then based on that the list view columns should be populated. Example: Select percent from 100-80 % then columns 1 and 2 get populated. Select total trans from <1000 and then column 3 and 4 get populated.
Problem:
When adding items to my listview, say I select option 1 from menu strip. Then columns 1 and 2 get filled up. Good. BUT right after if I select option from total trans, the column 3 and 4 do get filled up, BUT there is a ton of WHITEspace in the columns. Basically, the columns should not have white space when getting filled up.
This is what I mean: Notice the whitespace (outlined by red). First I selected option 1, then as soon as I selecte the option to populate column 3 and 4, they filled, but with whitespace. The column values for col 3 and 4 should have nothing above them in white.
Also here: I select another option from menu strip (for col 1 and 2) , after selecting the option to fill column 3 and 4, and more whitespace:
Code:
// Fill Column 1 and 2 from option 1
private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
int totItems = Seq3.Count - 1;
if (PercentPopTolerance1.Count - 1 > totItems) totItems = PercentPopTolerance1.Count - 1;
for (int i = 0; i <= totItems; i++)
{
ListViewItem lvi = new ListViewItem();
string item1 = "";
string item2 = "";
if (Seq3.Count - 1 >= i) item1 = Seq3[i].ToString();
if (PercentPopTolerance1.Count - 1 >= i) item2 = PercentPopTolerance1[i].ToString();
lvi.SubItems.Add(item1);
lvi.SubItems.Add(item2);
listView2.Items.Add(lvi);
}
}
// Percent tolerance from 80-60%
// Fill Column 1 and 2 from option 2
private void toolStripMenuItem3_Click_1(object sender, EventArgs e)
{
ClearColumn("columnHeader5");
int totItems = Seq4.Count - 1;
if (PercentPopTolerance2.Count - 1 > totItems) totItems = PercentPopTolerance2.Count - 1;
for (int i = 0; i <= totItems; i++)
{
ListViewItem lvi = new ListViewItem();
string item1 = "";
string item2 = "";
if (Seq4.Count - 1 >= i) item1 = Seq4[i].ToString();
if (PercentPopTolerance2.Count - 1 >= i) item2 = PercentPopTolerance2[i].ToString();
lvi.SubItems.Add(item1);
lvi.SubItems.Add(item2);
listView2.Items.Add(lvi);
}
}
// Fill columns 3 and 4 from option in total trans menustrip
// Total trans tolerance < 1000
private void etcToolStripMenuItem_Click(object sender, EventArgs e)
{
int totItems = YYMMt21.Count - 1;
if (TotalTransIrregularitiest21.Count - 1 > totItems) totItems = TotalTransIrregularitiest21.Count - 1;
for (int i = 0; i <= totItems; i++)
{
ListViewItem lvi = new ListViewItem();
string item1 = "";
string item2 = "";
if (YYMMt21.Count - 1 >= i) item1 = YYMMt21[i].ToString();
if (TotalTransIrregularitiest21.Count - 1 >= i) item2 = TotalTransIrregularitiest21[i].ToString();
// Skip first 2 columns
lvi.SubItems.Add(string.Empty);
lvi.SubItems.Add(string.Empty);
lvi.SubItems.Add(item1);
lvi.SubItems.Add(item2);
listView2.Items.Add(lvi);
}
}
EDIT: Trying User suggestion:
New problem arises: what is supposed to be in column 3 and 4 vertically now fills the listview from the first row horizontally:
My attempt:
private void etcToolStripMenuItem_Click(object sender, EventArgs e)
{
int totItems = YYMMt21.Count - 1;
if (TotalTransIrregularitiest21.Count - 1 > totItems) totItems = TotalTransIrregularitiest21.Count - 1;
for (int i = 0; i <= totItems; i++)
{
ListViewItem lvi = new ListViewItem();
string item1 = "";
string item2 = "";
if (YYMMt21.Count - 1 >= i) item1 = YYMMt21[i].ToString();
if (TotalTransIrregularitiest21.Count - 1 >= i) item2 = TotalTransIrregularitiest21[i].ToString();
// Skip first 2 columns
lvi.SubItems.Add(string.Empty);
lvi.SubItems.Add(string.Empty);
int rowToPopulate = 0;
int colToPopulate = 0;
if (rowToPopulate <= listView2.Items.Count - 1)
{
//Editing an existing row/ListViewItem
listView2.Items[rowToPopulate].SubItems.Insert(colToPopulate, new ListViewItem.ListViewSubItem() { Text = item1 });
// How would I also add item2?
// listView2.Items[rowToPopulate].SubItems.Insert(colToPopulate, new ListViewItem.ListViewSubItem() { Text = item2 });
}
else
{
//Adding a new row/ListViewItem
// ListViewItem lvi = new ListViewItem();
lvi.SubItems.Add(item1);
lvi.SubItems.Add(item2);
//Add all the other Subitems as usual
listView2.Items.Add(lvi);
}
//listView2.Items.Add(lvi);
}
}
You can alternate Items.Add and Items.SubItems.Insert. Sample code:
string curItem = "curVal";
int rowToPopulate = 0;
int colToPopulate = 0;
if (rowToPopulate <= listView2.Items.Count - 1)
{
//Editing an existing row/ListViewItem
listView2.Items[rowToPopulate].SubItems.Insert(colToPopulate, new ListViewItem.ListViewSubItem() { Text = curItem });
}
else
{
//Adding a new row/ListViewItem
ListViewItem lvi = new ListViewItem();
lvi.SubItems.Add(curItem);
//Add all the other Subitems as usual
listView2.Items.Add(lvi);
}
This code checks whether the row being analysed (rowToPopulate) has already been populated or not. If it hasn't been populated yet, you can use the code so far (under the else statement, including as many SubItems as columns by letting blank the ones for which no information is available). If this is the second time that you analyse this row (some columns have already been populated and some other ones were left blank), you take the given ListViewItem/row (listView2.Items[rowToPopulate]) and insert the new element in the SubItem/column you wish.
From what I can tell on your code, you just keep adding more items on the events, supplying String.Empty for the columns you're trying to skip. If you want the new values from the next event to appear on the same row as values already present, you'll have to update the already existing rows SubItems and supply which values are to go on which row.
Edit: I accomplished what you're trying to do with the below sample. This assumes that you know or can get the size of each list you're trying to add to each column. You'll have to implement some null checking etc to make sure you're not referencing null cells in the lists. Just expand on this sample and tailor it to your exact situation.
List<string> sList = new List<string>() { "1", "2", "3", "4", "5" };
List<string> lList = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8" };
private void button1_Click(object sender, EventArgs e)
{
listView1.Clear();
addColumns();
for (int i = 0; i < sList.Count(); i++)
{
var item1 = new ListViewItem(sList[i]);
item1.SubItems.Add(String.Empty);
listView1.Items.Add(item1);
}
}
private void button2_Click(object sender, EventArgs e)
{
listView1.Clear();
addColumns();
for (int i = 0; i < lList.Count(); i++)
{
if (i < sList.Count())
{
var item2 = new ListViewItem(sList[i]);
item2.SubItems.Add(lList[i]);
listView1.Items.Add(item2);
}
else
{
var item3 = new ListViewItem(String.Empty);
item3.SubItems.Add(lList[i]);
listView1.Items.Add(item3);
}
}
}
private void addColumns()
{
listView1.Columns.Add("Column 1", -2, HorizontalAlignment.Left);
listView1.Columns.Add("Column 2", -2, HorizontalAlignment.Left);
}

Remove a checkbox that is being created dynamically in a loop

I have a bunch of code that dynamicly creates some controls. It looks in a folder and lists the filenames in it. For each file in the folder it creates a checklistbox item, listbox item and two checkboxes. This is working great and as intended:
private void getAllFiles(string type)
{
try
{
string listPath = "not_defined";
if (type == "internal_mod")
{
int first_line = 76;
int next_line = 0;
int i = 0;
CheckBox[] chkMod = new CheckBox[100];
CheckBox[] chkTool = new CheckBox[100];
listPath = this.internalModsPath.Text;
string[] filesToList = System.IO.Directory.GetFiles(listPath);
foreach (string file in filesToList)
{
if (!internalModsChkList.Items.Contains(file))
{
internalModsChkList.Items.Add(file, false);
string fileName = Path.GetFileName(file);
internalModNameList.Items.Add(fileName);
//-----------------
// Draw Checkboxes
//-----------------
chkMod[i] = new CheckBox(); chkTool[i] = new CheckBox();
chkMod[i].Name = "modChk" + i.ToString(); chkTool[i].Name = "modChk" + i.ToString();
//chkMod[i].TabIndex = i; //chkTool[i].TabIndex = i;
chkMod[i].Anchor = (AnchorStyles.Left | AnchorStyles.Top); chkTool[i].Anchor = (AnchorStyles.Left | AnchorStyles.Top);
chkMod[i].Checked = true; chkTool[i].Checked = false;
chkMod[i].AutoCheck = true; chkTool[i].AutoCheck = true;
chkMod[i].Bounds = new Rectangle(549, first_line + next_line, 15, 15); chkTool[i].Bounds = new Rectangle(606, first_line + next_line, 15, 15);
groupBox7.Controls.Add(chkMod[i]); groupBox7.Controls.Add(chkTool[i]);
//-----------------
next_line += 15;
i++;
}
}
}
Now my problem is that I also want the user to be able to delete all these thing again based on the checklistbox' checked items.. I have no problems deleting the items in the checklistbox or the items in the listbox, but I want to remove the two checkboxes I create too ..
This is what I got to remove the items in the checklistbox, and the listbox
private void internalModListDel_btn_Click(object sender, EventArgs e)
{
int count = internalModsChkList.Items.Count;
for (int index = count; index > 0; index--)
{
if (internalModsChkList.CheckedItems.Contains(internalModsChkList.Items[index - 1]))
{
internalModsChkList.Items.RemoveAt(index - 1);
internalModNameList.Items.RemoveAt(index - 1);
groupBox7.Controls.Remove(modChk[index - 1]);
}
}
}
As you can see I have also tried to write something to remove the checkbox but it doesn't work and I have no idea how to make it work
Can you assist ?
Try using UserControls.
Use the ListBox controller to show those UserControls,
The user control can be built with those checkboxes, and the labels you want .
Another suggestion is to bind this list to an ObservableCollection which will contain the UserContorols you have created.
This way, it will be much more simlpe to add/remove/change the items inside.

Delete dynamically created controls

I keep getting stuck on this part of my program.
whenver i call an listbox.selectitemchange event, i want the proper amount of trackbar and labels to be displayed.
now, it does not work properly.
Some of them get removed when the event is called, some of them don't.
foreach (Label label in Controls.OfType<Label>())
{
if (label.Tag != null && label.Tag.ToString() == "dispose")
{
label.Dispose();
}
}
foreach (TrackBar trackBar in Controls.OfType<TrackBar>())
{
if (trackBar.Tag != null && trackBar.Tag.ToString() == "dispose")
{
trackBar.Dispose();
}
}
for (int i = 0; i < calc; i++)
{
//string[] LineWidthSplitted = lines[lineWidth].Split(' ');
//Int32.TryParse(LineWidthSplitted[2], out WidthValue);
Label Label = new Label();
Label.Name = "TrackbarWidth" + LabelName++;
Label.Tag = "dispose";
Label.Text = "Board -" + LabelName + "- Height:";
Label.Location = new Point(10, 450 + (50 * LabelName));
Label.Size = new System.Drawing.Size(100, 25);
this.Controls.Add(Label);
TrackBar trackBar = new TrackBar();
trackBar.Name = "TrackbarWidth" + trackbarName++;
trackBar.Tag = "dispose";
trackBar.Maximum = 85;
trackBar.Minimum = 65;
trackBar.SmallChange = 5;
trackBar.TickFrequency = 5;
trackBar.Value = 65;
trackBar.Location = new Point(150, 450 + (50 * trackbarName));
trackBar.Size = new System.Drawing.Size(100, 25);
this.Controls.Add(trackBar);
lineWidth += 4;
}
while, when i remove the foreach for the trackbar, all labels get properly displayed.
they all get deleted, en recreated for the pricese amount needed to be created, no exceptions.
Any reason why?
thank you.
Don't use "Dispose" on the labels right away. First remove them. Note that you can't modify the Controls collection inside the foreach so you have to do something like this:
List<Label> itemsToRemove = new List<Label>();
foreach (Label label in Controls.OfType<Label>())
{
if (label.Tag != null && label.Tag.ToString() == "dispose")
{
itemsToRemove.Add(label);
}
}
foreach (Label label in itemsToRemove)
{
Controls.Remove(label);
label.Dispose();
}
If you want to remove all different kinds of controls in one swoop:
List<Control> itemsToRemove = new List<Control>();
foreach (Control ctrl in Controls)
{
if (ctrl.Tag != null && ctrl.Tag.ToString() == "dispose")
{
itemsToRemove.Add(ctrl);
}
}
foreach (Control ctrl in itemsToRemove)
{
Controls.Remove(ctrl);
ctrl.Dispose();
}
I can't test this now, but I think you should also remove the controls from the Form Controls collection where you have added them. By the way, in your case I think you could avoid the OfType extension and use the old fashioned for..loop that will allow to execute just one loop....
for(int x = this.Controls.Count - 1; x >= 0; x--))
{
Control ctr = this.Controls[x];
if (ctr Is Label && ctr.Tag != null && ctr.Tag.ToString() == "dispose")
{
this.Controls.Remove(ctr);
ctr.Dispose();
}
if(ctr Is TrackBar && ctr.Tag != null && ctr.Tag.ToString() == "dispose")
{
this.Controls.Remove(ctr);
ctr.Dispose();
}
}
Notice how removing elements from a collection with a for..loop should be done in reverse order, from the end to start of the collection

Enable and Disable ToolStripMenu entries in C#?

I have a Form that contains a Menu with two entries, which are Menu and Tools. The two Menues have some SubMenus.
Now i have a TextBox called txtSelect and a Button called btnVisible, if I enter 1,2 in the TextBox, the SubMenus in the Menu should not be visible. I written the following code, bit it is Hard-written.
ToolStripMenuItem[] mstrip = new ToolStripMenuItem[] { msO1, msO2, msO3, msP1, msP2, msP3 };
if (txtSelect.Text.Length > 2)
{
string word = txtSelect.Text;
string[] splt = word.Split(',');
for (int x = 0; x < mstrip.Length; x++)
mstrip[x].Visible = true;
for (int x = 0; x < splt.Length; x++)
{
int y = Convert.ToInt32(splt[x].ToString()) - 1;
if (y >= 0 && y < mstrip.Length)
mstrip[y].Visible = false;
textBox1.AppendText(mstrip[y].Text);
textBox2.AppendText(mstrip[y].OwnerItem.Text);
}
}
I want to use foreach loop instead in a Button Click Event and have attempted with the following, however the result is not the same as with the code above.
foreach (ToolStripMenuItem mnItem in msMenus.Items)
{
MessageBox.Show(mnItem.Text);
for (int i = 0; i < mnItem.DropDown.Items.Count; i++)
{
MessageBox.Show(mnItem.DropDown.Items[i].Text);
mnItem.DropDown.Items[i].Visible = true;
}
}
Well, may be you want something like :
List<Int32> lstindex = new List<Int32>();
String[] splt = txtSelect.Text.Split(',');
// initialize list of indexed for textbox
foreach (String str in splt)
{
lstindex.Add(Convert.ToInt32(str) - 1);
}
// for each menu
foreach (ToolStripMenuItem mnItem in msMenus.Items)
{
// for each menu item
foreach (ToolStripItem item in mnItem.DropDown.Items)
{
// if index of item is in the list of indexed, set visible to false, otherwise to true
item.Visible = lstindex.Contains(mnItem.DropDown.Items.IndexOf(item)) ? false : true;
}
}

Categories

Resources