Trying to close all forms except for the main menu using
FormCollection formsList = Application.OpenForms;
with a foreach loop and saying,
if (thisForm.Name != "Menu") thisForm.Close();
Which works ok, it skips the menu, and closes the first, but it then errors:
Collection was modified; enumeration operation may not execute
and stops. I have tried a few places, and they all say that this foreach loop is the way to do it, and it is especially annoying as I am not updating my forms list after closing the forms, which I thought might work. The only thing I could think of was to start at the back and work forward using a while.
If you use foreach to enumerate through a collection, it can't be modified (items added or removed) during the iteration. Try copying references to the forms to another collection, and then remove them by iterating through that collection.
In situations like this, you can use a list or a simple array, such as:
List<Form> openForms = new List<Form>();
foreach (Form f in Application.OpenForms)
openForms.Add(f);
foreach (Form f in openForms)
{
if (f.Name != "Menu")
f.Close();
}
Or you can use a for loop:
for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
{
if (Application.OpenForms[i].Name != "Menu")
Application.OpenForms[i].Close();
}
Or, my new and current favorite, you can use the Reverse() method:
foreach (Form f in Application.OpenForms.Reverse())
{
if (f.Name != "Menu")
f.Close();
}
Here's an even more concise method that uses the same number of lines as your original method:
Form[] forms = Application.OpenForms.Cast<Form>().ToArray();
foreach (Form thisForm in forms)
{
if (thisForm.Name != "Menu") thisForm.Close();
}
By using Linq's extension method Cast, you can avoid looping through the collection to build an array.
That happens when the collection is changed inside a foreach loop that uses it. You are removing an item from formsList inside the loop.
Try this:
for (int i = formsList.Count-1; i > 0; i--)
{
if (formsList[i].Name != "Menu")
{
formsList[i].Close();
}
}
To Close all forms :
for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
{
if (Application.OpenForms[i].Name != "Menu")
Application.OpenForms[i].Close();
}
I know this is old but I needed to perform this same scenario and came up with a elegant and simple way to achieve this as follows
Form[] formsList = Application.OpenForms.Cast<Form>().Where(x => x.Name == "Form1").ToArray();
foreach (Form openForm in formsList)
{
openForm.Close();
}
This will close ALL windows that where opened called Form1
As the error states, you can't modify a collection in its foreach.
Instead, you can use a backwards for loop.
as the form collection is updating for every iteration. When you close a form, it is removed from the form collection. it's like removing a object from memory while it is using .
Related
I have this code and it seems like it should work but for some reason it doesnt.
foreach(Control c in Controls.OfType<CheckBox>()) {
Check_checked += 1;
}
** Check edit: When I step through the code the Control c is picking up all of the textboxes but nothing else.
Any help would be appreciated.
**I realise that ofType should not be picking up text/labels/watever.
Im getting text/labels/watever.
I moved my code to another computer also running visual studio. It doesnt pickup checkboxes and it doesnt seem to be bugging about picking up text/label/watever on that system.
So I think one of the issues is my VS is bugger-up. Will re-install now.
look at following answer
Get all controls of a specific type
Code check if the control is a ContainerControl and then iterates over all the children of that control to find the controls of type.
public static IEnumerable<T> GetControlsOfType<T>(Control root)
where T : Control
{
var t = root as T;
if (t != null)
yield return t;
var container = root as ContainerControl;
if (container != null)
foreach (Control c in container.Controls)
foreach (var i in GetControlsOfType<T>(c))
yield return i;
}
Then you could do something like this:
foreach (var pictureBox in GetControlsOfType<CheckBox>(form)) {
}
This code is a simple solution for counting checkboxes on a form that are checked.
private int CountChecks(IEnumerable controls)
{
var result = 0;
foreach (Control xControl in controls)
{
if (xControl.HasChildren) result += CountChecks(xControl.Controls);
if (!(xControl is CheckBox)) continue;
if (!(xControl as CheckBox).Checked) continue;
result++;
}
return result;
}
you might use this in this manner:
var howManyAreChecked = CountChecks(Controls);
This would have to be in a form to use this syntax. You must pass a forms Controls into the method in order to work correctly.
in my software I have a TabControl and I want to search for all Controls of a certain type using foreach (for example) and change some of there properties.
Now when I do something like this:
Control ctrl;
ctrl = My_CheckedListBox;
((CheckedListBox)ctrl).SetItemChecked(0, false);
It works fine and the checkboxes all get unchecked, no problem. But when I use foreach they don't:
foreach (Control item in ModuleTab.Controls)
{
if (item is CheckedListBox)
{
for (int i = 0; i < ((CheckedListBox)item).Items.Count; i++)
{
((CheckedListBox)item).SetItemChecked(i, false);
}
}
}
I know it is because item is not really the Control but an object. I wonder if there is something like:
foreach (Control ref item in ModuleTab.Controls)
Any help?
So I solved it! Jon Skeet was right. The Tab in the TabControl didn't contain the controls I was looking for because they were all in a GroupBox!
I searched for them in the GroupBox and there they were. The CheckBoxes where unchecked and everything worked perfectly as it should.
#Jon Thx for the "observing" tip. I let the program show me the available Controls in the Tab using a MessageBox and that is how I solved it. Thx also for the user who deleted his answer because he interduced me to the OfType<>() method.That really made the code look better:
foreach (var groupbox in ModuleTab.Controls.OfType<GroupBox>())
{
foreach (var item in groupbox.Controls.OfType<CheckedListBox>())
{
for (int i = 0; i < item.Items.Count; i++)
{
item.SetItemChecked(i, false);
}
}
}
foreach (Control item in ModuleTab.Controls)
{
if (item.GetType() == typeof(CheckedListBox)))
{
for (int i = 0; i < item.Items.Count; i++)
{
item.SetItemChecked(i, false);
}
}
}
I have 15 list box inside my winForm... I am using to clear each of them like below. is there any way to clear them in short way when the application starts?
listBox1.Items.Clear();
listBox2.Items.Clear();
listBox3.Items.Clear();
listBox4.Items.Clear();
listBox5.Items.Clear();
listBox6.Items.Clear();
listBox7.Items.Clear();
listBox8.Items.Clear();
listBox9.Items.Clear();
listBox10.Items.Clear();
parentControl.Controls.OfType<ListBox>().ToList().ForEach(l => l.Items.Clear());
If you want to clear all listboxes for the entire form you can use something like this:
foreach(var listbox in this.Traverse().OfType<ListBox>())
listbox.Items.Clear();
There I use the Traverse helper method to get all of the children, recursively, of a particular control. Here is its implementation:
public static IEnumerable<Control> Traverse(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control children in next.Controls)
stack.Push(children);
yield return next;
}
}
If you don't want to clear all listboxes, but rather only those specified list boxes, then you can try to put all of them into some container, and then traverse that container (rather than the whole form), or if that's not possible you can put them all in a collection and iterate through that collection, i.e.:
ListBox[] listboxes = new[]{
listBox1,
listBox2,
//...
};
foreach(var listbox in listboxes)
listbox.Items.Clear();
If all the controls are in the same form(lets call this form1)
foreach (Control c in form1.Controls)
{
if (c is ListBox)
{
((ListBox)c).Clear();
}
}
If you are finding yourself clearing them over and over in your code and you are wishing to partition the work out, make a method call to cut down on code reuse.
private void ClearList()
{
listBox1.Items.Clear();
listBox2.Items.Clear();
listBox3.Items.Clear();
listBox4.Items.Clear();
listBox5.Items.Clear();
listBox6.Items.Clear();
listBox7.Items.Clear();
listBox8.Items.Clear();
listBox9.Items.Clear();
listBox10.Items.Clear();
}
else, put them into a collection.
I have the following loop to remove the buttons in my C# Windows Forms application. The only problem is that it skips every other button. How do I go about removing all the button controls from my form?
foreach (Control cntrl in Controls)
{
if(cntrl.GetType() == typeof(Button))
{
Controls.Remove(cntrl);
cntrl.Dispose();
}
}
I think this way is a bit more readable:
var controlsToRemove = Controls.OfType<Button>().ToArray();
foreach (var control in controlsToRemove)
{
Controls.Remove(control);
cntrl.Dispose();
}
Calling ToArray() makes a new concrete collection, so that you can enumerate over one and modify the other.
Surprised that's not erroring on you, since you're modifying the collection as you're iterating over it. Use a for loop and start at the end:
for (int ii = Controls.Count - 1; ii >= 0; ii--)
{
Control cntrl = Controls[ii];
Controls.remove(cntrl);
cntrl.Dispose();
}
(Starting at the end because otherwise you'd be changing the indexes of each control as you iterated.)
Youre iterating over the same collection from whitch youre removing. Use this code:
List<Control> cleanControls = new List<Control>();
foreach(Control ctr in Controls)
{
if(cntrl.GetType() != typeof(Button))
{
cleanControls.Add(ctr);
}
else
{
ctr.Dispose();
}
}
Controls = cleanControls;
That's It!
Hope I helped!
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# Not Disposing controls like I told it to
I use this to delete all Pictureboxes I created on my Winform
foreach (PictureBox pb in this.Controls)
{
pb.Dispose();
}
but each time only about the half of all Pictureboxes get disposed,
the other half remains untouched
i solved it by surrounding it with a while (Controls.OfType<PictureBox>().Count() > 0 ) Loop
so it just gets executed until all Pictureboxes are gone,
but this is a bad solution and i want to know why this happens and how i can solve it the right way
Control.Dispose is removing the control from the collection, changing the index and messing up the foreach.
You can solve this problem by using a reverse for loop or by creating a separate array holding the references.
Solution #0
for (int i = Controls.Count - 1; i >= 0; i--)
{
Controls[i].Dispose();
}
Solution #1
foreach (var control in Controls.Cast<Control>().ToArray())
{
control.Dispose();
}
Because you have controls that are not a PictureBox, you loop will throw exception when it gets to this control. Fix your loop like that:
foreach (PictureBox pb in this.Controls.OfType<PictureBox>().ToArray())
{
pb.Dispose();
this.Controls.Remove(pb);
}
Try With:
foreach(Control c in this.Controls)
{
if(c is PictureBox)
{
c.Dispose();
this.Controls.Remove(c);
}
}