So I am trying to loop though items that are in a listbox in my application. The list box will allow you to select multiple items to which I have a method tied to each item in the listbox. I have a counter variable incremented each time the loop works.When I use the foreach loop with the switch statement below, it does the first item correct, but then loops through the same item again. I know I am missing something as it is supposed to go to the next item in the listbox and not the same item.
string reportname = lstbxReports.SelectedValue.ToString();
int i = 0;
foreach (var report in reportname)
{
switch (reportname)
{
case "Overview":
{
if (i < 1)
{
PrintOverview(filename);
}
else if (i >= 1)
{
PrintOverviewAppend(filename);
}
break;
}
case "Sources":
{
if (i < 1)
{
PrintSource(filename);
}
else if (i >= 1)
{
PrintSourceAppend(filename);
}
break;
}
}
i++
Any thoughts or suggestions on how I can get the foreach loop to go to the next item in the selected listbox?
Also, this is just a snippet as I have about 11 case items to loop through.
You probably want to switch on report, not reportname.
foreach(string item in listBox.Items)
{
}
?
Depends on how you setup the data source for the listbox though (I'm assuming this is WinForm?). If you created it by adding .Items or using the designer then this will work. However if you've used .DataSource then it wont work.
I'd personally have a
List<string> list = SomeMethodWhereIMakeTheList();
and set that to:
listbox.DataSource = list;
then I wouldn't even have to touch the ListBox to mess with the contents:
list.ForEach(...)
Don't do the print logic in a foreach. Split out the data then print such this (note I changed the name of reportname to reportnames to signify a list of items)
string reportnames = lstbxReports.SelectedValue.ToString();
var firstReport = reportnames.First(); // No error checking here, would use FirstOrDefault with null checks.
if (firstReport == "OverView")
PrintOverview(filename);
else
PrintSource(filename);
// Now print out the rest
reportnames.Skip(1)
.ToList()
.ForEach(rp =>
{
if (rp == "OverView")
PrintOverviewAppend(filename);
else
PrintSourceAppend(filename);
});
Related
How can I delete the current element of an array inside a foreach-loop?
My program gets data form a DB and sends it to a new one via HTTP requests. Now I want to post a JSON string to my new DB. If it was a success I want to delete the current array item which I'm working with. Something like this.
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == true)
{
//delete current item
}
}
since you cannot delete items from an array and change the size of it here is a loop approach using a second collection
List<itemClass> keepCollection = new List<itemClass>();
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == false)
{
keepCollection.Add(item);
}
}
If you need it again in array form just call ToArray()
var finalResult = keepCollection.ToArray();
appraoch with Linq which creates a new array with valid elements and overwrites the existing array
array = array.Where(x => !method.DoSomething(x)).ToArray(); //select valid elements
there are 2 ways (both were tested)
foreach (var item in array.ToList())
{
bool decide = method.DoSomething();
if (decide == true)
{
item.Remove();
}
}
and
for ( i=0; i < array.Length; i++)
{
bool decide = method.DoSomething();
if (decide == true)
{
array[i].Remove();
}
}
Whenever you want to delete entries from a collection, you should never loop through that collection from beginning to end, but always from end back to beginning.
By the way, C# does not allow you deleting entries from a collection while looping through that collection using a foreach loop.
I need to prevent Duplicate entries from ListView controller by column text. if duplicate found I need to get the ListView Item for further process. I saw every one says
ListViewItem item = ListView3.FindItemWithText("test");
if (!listView1.Items.ContainsKey(txt))
{
// doesn't exist, add it
}
but how can I point which Column text?? I did prevent duplicates by adding ids into a array and after check array value exists. but in that case I can find which entry duplicated.
this is my code.
rd = cmd.ExecuteReader();
// Validation not working - duplicating ListviewItems
while (rd.Read()) {
ListViewItem lvvi = new ListViewItem(rd.GetString(0));
lvvi.SubItems.Add(rd.GetString(1));
lvvi.SubItems.Add(rd.GetString(5));
lvvi.SubItems.Add("1");
lvvi.SubItems.Add(rd.GetString(0));
int listViewItemID;
int[] ids;
ids = new int[100];
if (listView3.Items.Count > 0)
{
int addingItemID;
//ADD ListView ids into array
int i=0;
foreach (ListViewItem li in listView3.Items)
{
listViewItemID = Int32.Parse(li.SubItems[0].Text);
addingItemID = Int32.Parse(rd.GetString(0));
ids[i] = listViewItemID;
i++;
}
//Check item allready exsist
if (ids.Contains(Int32.Parse(rd.GetString(0))))
{
MessageBox.Show("sdsd");
}
else {
listView3.Items.Add(lvvi);
}
}
else {
listView3.Items.Add(lvvi);
}
}
//Calculate Price summery
this.calculatePrice();
Instead of looping to get all id's, you can loop through the items or use linq to find the specific id and keep the result. This can be done in an external function or by replacing the ids portion with the loop or use something like FirstOrDefault:
addingItemID = rd.GetString(0);
ListViewItem existing = listView3.Items.Cast<ListViewItem>().FirstOrDefault(li => li.SubItems[0].Text == addingItemID); //(not sure if the cast is needed)
if (existing != null)
{
//item exists, variable existing refers to the item
MessageBox.Show("sdsd");
}
else
{
listView3.Items.Add(lvvi);
}
I am trying to remove more items from a combobox but the application is only removing one item at a time.
The combobox has a list of email addresses. I want to remove empty items (""), and those that don't have # inside of text.
Code below only removes one item at a time.
for (int i = 0; i < cmbTo.Items.Count; i++)
{
string st = cmbTo.Items[i].ToString();
if (st == "" || st.IndexOf("#") == -1)
{
cmbTo.Items.RemoveAt(i);
}
}
How can I rewrite this?
Hint: Think about what happens to the i variable when you remove an item
...
When you RemoveAt an item, the item is removed, and every subsequent item moves up one index. Your loop then hits the bottom, where it goes back to the top, increments i, and moves on.
Result? You just skipped an item. If this is the last item in the list, then the loop exists.
Instead, manually decrement i to offset your removal, so that everything works:
for (int i = 0; i < cmbTo.Items.Count; i++)
{
string st = cmbTo.Items[i].ToString();
if (st == "" || st.IndexOf("#") == -1)
{
cmbTo.Items.RemoveAt(i);
i--;
}
}
Your code doesn't work because the moment you remove an item from the collection, the Count() decreases and the for loop exits before going through all the list of items.
You need to first create a list of elements to remove (put them in a temp list) and then iterate through the newly created list calling cmbTo.Items.Remove(currentElement);
When you remove an item from a combobox, the indices of the following items change and your item count will change. Could that account for the behavior you're seeing?
Just do the removal in the opposite direction (i.e. from the end to the front), and you won't need to worry about adjusting i1 when the item is removed:
var items = cmbTo.Items;
int i = items.Count;
while (i > 0) {
--i;
string st = items[i].ToString();
if (st == "" || st.IndexOf("#") < 0)
items.RemoveAt(i);
}
1 Which you currently don't do, so some items that should potentially be removed are skipped, which causes your problem.
I had learnt by reading your great answers here, that it is not good practice deleting items from within a foreach loop, as it is (and I quote) "Sawing off the branch you're sitting on".
My code currently removes the text from the dropdownlist, but the actual item remains (just without text displayed).
In other words, it isn't deleting, and probably can't because you can't delete from within a foreach loop.
After hours of trying I am unable to get my head around a way of doing it.
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
//this is the foreach loop
foreach (ToolStripItem mItem in favoritesToolStripMenuItem.DropDownItems)
{
//This rules out seperators
if (mItem is ToolStripMenuItem)
{
ToolStripMenuItem menuItem = mItem as ToolStripMenuItem;
//This matches the dropdownitems text to the CheckedItems String
if (((ToolStripMenuItem)mItem).Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
//And deletes the item
menuItem.DropDownItems.Remove(mItem);
}
}
}
}
But it isn't deleting because it is within a foreach loop!
I would greatly appreciate your help, and be truly amazed if anyone can get their head around this code :)
Kind Regards
Fun with LINQ!
// Loop through the checked items, same as you did.
foreach (var checkedItem in this.organizeFav.CheckedItems)
{
// Cast from IEnumerable to IEnumerable<T> so we can abuse LINQ
var matches = favoritesToolStripMenuItem.DropDownItems.Cast<ToolStripItem>()
// Only items that the Text match
.Where(item => item.Text == checkedItem.Text)
// Don't match separators
.Where(item => item is ToolStripMenuItem)
// Select the keys for the later .Remove call
.Select(item => item.Name);
// Loop through all matches
foreach (var key in matches)
{
// Remove them with the Remove(string key) overload.
favoritesToolStripMenuItem.Remove(key);
}
}
You don't need a foreach loop - just use a regular loop but go in reverse, start at the end and go to the beginning.
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
//this *replaces* the foreach loop
for(int j = favoritesToolStripMenuItem.DropDownItems.Count - 1; j >= 0; j--)
{
ToolStripMenuItem menuItem = favoritesToolStripMenuItem.DropDownItems[j] as ToolStripMenuItem;
//This rules out seperators
if (menuItem != null)
{
//This matches the dropdownitems text to the CheckedItems String
if (menuItem.Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
favoritesToolStripMenuItem.DropDownItems.Remove(menuItem);
}
}
}
}
this was #Kurresmack's code rearranged, i just coded it directly here in the page so excuse any small syntax error or anything obvious i overlooked (disclaimer: it is a sample!!)
You can still treat favoritesToolStripMenuItem.DropDownItems as a collection like you were, but you don't have to enumerate over it using a foreach. This cuts down on a few lines of code, and it works because you are iterating it in reverse order, you will not get an index out of bounds exception.
Try something like this:
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
List<ToolStripItem> toRemove = new List<ToolStripItem>();
//this is the foreach loop
foreach (ToolStripItem mItem in favoritesToolStripMenuItem.DropDownItems)
{
//This rules out seperators
if (mItem is ToolStripMenuItem)
{
ToolStripMenuItem menuItem = mItem as ToolStripMenuItem;
//This matches the dropdownitems text to the CheckedItems String
if (((ToolStripMenuItem)mItem).Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
toRemove.Add(mItem);
}
}
}
foreach(var item in toRemove)
{
favoritesToolStripMenuItem.DropDownItems.Remove(item);
}
}
To my mind, the way to make the code work is:
1. Create an instance of the type the favoritesToolStripMenuItem.DropDownItems collection is.
2. In the foreach loop, add all items, you do not want to be removed, to that collection.
3. Make favoritesToolStripMenuItem.DropDownItems to point to the new collection. Or clear favoritesToolStripMenuItem.DropDownItems and load the items from the new collection to it.
Hope this helps
Instead of a foreach use a reverse for-Loop:
for(int reverseIndex = myList.Count - 1; reverseIndex >= 0; reverseIndex--)
{
var currentItem = myList[reverseIndex];
if(MatchMyCondition(currentItem))
{
myList.Remove(currentItem);
}
}
I am trying to do this:
foreach (Settings sets in MySets)
{
if (sets.pName == item.SubItems[2].Text)
{
var ss = new SettingsForm(sets);
if (ss.ShowDialog() == DialogResult.OK)
{
if (ss.ResultSave)
{
sets = ss.getSettings();
}
}
return;
}
}
But since the sets spawned variable is readonly, I cant override it.
I would also like to do something like this
foreach (Settings sets in MySets)
{
if(sets.pName == someName)
sets.RemoveFromList();
}
How can I accomplish this? Lists have a very nice Add() method, but they forgot the rest :(
You can use:
MySets.RemoveAll(sets => sets.pName == someName);
to remove all the items that satisfy a specific condition.
If you want to grab all the items satisfying a condition without touching the original list, you can try:
List<Settings> selectedItems = MySets.FindAll(sets => sets.pName == someName);
foreach loops don't work here as trying to change the underlying list will cause an exception in the next iteration of the loop. Of course, you can use a for loop and manually index the list. However, you should be very careful not to miss any items in the process of removing an item from the list (since the index of all the following items will get decremented if an element is removed):
for (int i = 0; i < MySets.Count; ++i) {
var sets = MySets[i]; // simulate `foreach` current variable
// The rest of the code will be pretty much unchanged.
// Now, you can set `MySets[i]` to a new object if you wish so:
// MySets[i] = new Settings();
//
// If you need to remove the item from a list and need to continue processing
// the next item: (decrementing the index var is important here)
// MySets.RemoveAt(i--);
// continue;
if (sets.pName == item.SubItems[2].Text)
{
var ss = new SettingsForm(sets);
if (ss.ShowDialog() == DialogResult.OK)
{
if (ss.ResultSave)
{
// Assigning to `sets` is not useful. Directly modify the list:
MySets[i] = ss.getSettings();
}
}
return;
}
}
You can't do it in a 'regular' for loop?