473,804 Members | 4,217 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Problem with GetEnumeration in Foreach loop

tlhintoq
3,525 Recognized Expert Specialist
Situation:
A Windows Form with a single User Control containing 4 custom controls. This lets me drop the User Control on its own form to make it a dialog, or make it part of a larger configuration dialog with several such User Controls.
Each custom control has a property for COM port name (serial port).
Each custom control has a method for writing the settings.
The user control has a GetEnumeration structure so it can be easily referenced with a foreach (myCustomContro l Bob in myUserControl) type reference.

Errant behavior:
If I walk through using the foreach construct everything executes correctly THEN all the custom controls are inexplicably rendered invisible. They still exist because I can step through the save function and they all perform their methods. They just become hidden on the Windows form.
Yet - If I walk through the same steps with a for (int a = [...]) type construct doing exactly the same things everything executes and the custom controls are NOT hidden.

Its not like the loop is doing a thousand tasks and the process is hard to follow:
1) Set port name
2) Write settings

It really shouldn't matter *how* the set of controls is looped through. Both the 'for' and 'foreach' *should* being doing exactly the same thing: Loop through the four controls, set the port name property and execute the WritePrefs() method. That's it. Yet for some reason after the 'foreach' completes all the custom controls go invisible.

Something in the bowels of the foreach seems to be reacting to the custom controls .Show() and .Hide()

Does anyone have a clue or has anyone seen something like this before?
Obviously I have already worked around the weird behavior by using a 'for' instead of 'foreach'. But I'd like to understand the actual problem so I can use the more elegant 'foreach' in the future.

The foreach that hides the custom controls
Expand|Select|Wrap|Line Numbers
  1.             foreach (Saint.Controls.Devices.DeviceCtrl pebbles in Controller1)
  2.             {
  3.                 pebbles.myObject.PortName = myCOMport.PortName;
  4.                 pebbles.myObject.WritePrefs();
  5.             }
  6.  
The for that does the same thing without hiding the custom controls.
Expand|Select|Wrap|Line Numbers
  1.             for (int a = 0; a < Controller1.Controls.Count; a++)
  2.             {
  3.                 ((Saint.Controls.Devices.DeviceCtrl) Controller1.Controls[a]).myObject.PortName = myCOMport.PortName;
  4.                 ((Saint.Controls.Devices.DeviceCtrl) Controller1.Controls[a]).myObject.WritePrefs();
  5.             }
  6.  
The class with the GetEnumeration
Expand|Select|Wrap|Line Numbers
  1.     public partial class Controller1: UserControl, IEnumerable, IEnumerator
  2.     {
  3.         ArrayList AllControls = new ArrayList();
  4.  
  5.         #region GetEnumeration
  6.         int Position = -1;
  7.  
  8.         public IEnumerator GetEnumerator()
  9.         {
  10.             return (IEnumerator)this;
  11.         }
  12.         public bool MoveNext()
  13.         {
  14.             if (Position < AllControls.Count - 1)
  15.             {
  16.                 ++Position;
  17.                 return true;
  18.             }
  19.             Reset();
  20.             return false;
  21.         }
  22.         public void Reset()
  23.         {
  24.             Position = -1;
  25.         }
  26.         public object Current
  27.         {
  28.             get
  29.             {
  30.                 return AllControls[Position];
  31.             }
  32.         }
  33.         #endregion GetEnumeration
  34.  
  35.  
  36.         #region Construction
  37.         public Controller1()
  38.         {
  39.             InitializeComponent();
  40.             AllControls.Add(tcntrlWiper);
  41.             AllControls.Add(tcntlWasher);
  42.             AllControls.Add(tcntrlBlower);
  43.             AllControls.Add(tcntrlDefroster);
  44.         }
  45.         #endregion Construction
  46.     }
  47.  
Feb 5 '09 #1
9 2571
vekipeki
229 Recognized Expert New Member
When controls are hidden but still alive, it means they have been removed from the Control.Control s collection. But I don't see what could do that from your code (when comparing 'for' with 'foreach').

The strangest thing is that you don't even have to access your controls to have them removed from Controls collection, try this:

Expand|Select|Wrap|Line Numbers
  1. foreach (Control c in Controller1)
  2. {
  3.     // this will show 4
  4.     Console.WriteLine(Controller1.Controls.Count);
  5.  
  6.     // break immediately after getting the 1st control
  7.     break;
  8. }
  9. // this will write 0
  10. Console.WriteLine(Controller1.Controls.Count);
  11.  
That's pretty weird!

You can use foreach directly on Controller1.Con trols collection, instead of implementing IEnumerable, or implement IEnumerable just as a wrapper for Controls.GetEnu merator():

Expand|Select|Wrap|Line Numbers
  1. public IEnumerator GetEnumerator()
  2. {
  3.     return this.Controls.GetEnumerator();
  4. }
  5.  
(this last part doesn't solve the actual problem, but it works this way)
Feb 5 '09 #2
tlhintoq
3,525 Recognized Expert Specialist
I'm not convinced the controls are being removed - only because if I run the function a second time and step through it still loops four times receiving the Wiper, Washer, Blower, Defroster controls. If they were actually being removed the the controls collection after the first run of the function, wouldn't the second run have no controls to loop through?
Feb 5 '09 #3
Plater
7,872 Recognized Expert Expert
Both you made your foreach loop in the same format:
foreach (Control c in Controller1)

If I were to have used the loop, I would have said
foreach (Control c in Controller1.Con trols)

Not sure it matters, but I find it interesting that you don't actually reference your exact collection object?
Feb 5 '09 #4
tlhintoq
3,525 Recognized Expert Specialist
There was actually a reason for doing this way. My GetEnumeration is referencing only an array of custom controls. Right now that array is exactly the same as the controls collection. But I try to plan ahead and could see a time where the controls collection might include lables, pictureboxes, group boxes etc., yet I would want the foreach to only return the significant custom controls and not each and every little piece of decoration added to the User Control panel. Or maybe I only want to perform a function on *some* custom controls like controllable devices, but not all custom controls such as readout only sensors... So it made sense that I should be able to make a list of the objects that I want to be included in the enumeration and ignore others.

After some experimentation I found some more interesting results...
This works without hiding the controls... just as was suggested... by adding the reference to the .Controls collection.
Expand|Select|Wrap|Line Numbers
  1.             foreach (Saint.Controls.Devices.DeviceCtrl pebbles in Controller1.Controls)
  2.             {
  3.                 Console.WriteLine("TCCC: " + Controller1.Controls.Count);
  4.                 pebbles.myObject.PortName = myCOMport.PortName;
  5.                 pebbles.myObject.WritePrefs();
  6.             }
  7.  
So I figured Okay, I'll adjust the GetEnumeration to work with the controls collection to see what happens. It should be exactly the same since I am referencing the same array. So I comment out all the reference to my array and change lines 14 & 30 to directly reference the .Controls.

Expand|Select|Wrap|Line Numbers
  1.     public partial class Controller1: UserControl, IEnumerable, IEnumerator
  2.     {
  3.         //ArrayList AllControls = new ArrayList();
  4.  
  5.         #region GetEnumeration
  6.         int Position = -1;
  7.  
  8.         public IEnumerator GetEnumerator()
  9.         {
  10.             return (IEnumerator)this;
  11.         }
  12.         public bool MoveNext()
  13.         {
  14.             if (Position < this.Controls.Count - 1)
  15.             {
  16.                 ++Position;
  17.                 return true;
  18.             }
  19.             Reset();
  20.             return false;
  21.         }
  22.         public void Reset()
  23.         {
  24.             Position = -1;
  25.         }
  26.         public object Current
  27.         {
  28.             get
  29.             {
  30.                 return this.Controls[Position];
  31.             }
  32.         }
  33.         #endregion GetEnumeration
  34.  
  35.  
  36.         #region Construction
  37.         public ThrillControllerCntrl()
  38.         {
  39.             InitializeComponent();
  40.             //AllControls.Add(tcntrlWiper);
  41.             //AllControls.Add(tcntlWasher);
  42.             //AllControls.Add(tcntrlBlower);
  43.             //AllControls.Add(tcntrlDefroster);
  44.         }
  45.         #endregion Construction
  46.     }
  47.  
  48.  
Now that should have worked whether my foreach loop referenced the .Controls collection or not, right? - NOPE - It still hides the custom controls upon completion if I don't specify the .Collection in the foreach call construct.
-BUT- This does now remove the control from the collection: The second time through the function the collection count becomes zero.

As that weird or what?

Just to clarify one other thing... I got thinking that if I added a label(or something else) to the panel that maybe it wouldn't be a problem since the "foreach" call does specify an exact custom type. -NOPE- All controls of all types are returned and the code breaks as soon as it tries to cast a label to my custom type. - Which I had expected and why I wanted my GetEnumeration to only return exactly what I specified so I didn't have to filter at every point I used the call.
Expand|Select|Wrap|Line Numbers
  1. Unable to cast object of type 'System.Windows.Forms.Label' to type 'Saint.Controls.Devices.DeviceCtrl'.
  2.  
Seems rather limiting, but I can work around it - for now. I would have expected that my class should be able to specify what it will and won't return via my GetEnumeration method.
Feb 5 '09 #5
vekipeki
229 Recognized Expert New Member
That's my point exactly, your controls are removed from the Controls collection, not AllControls collection. Forms' controls are drawn only if they are part of the Controls collection.

Actually, your controls are removed from the Controls collection if you just implement IEnumerator, regardless of what it actually accesses -- check this out:

Expand|Select|Wrap|Line Numbers
  1. #region IEnumerable
  2.  
  3. public IEnumerator GetEnumerator()
  4. {
  5.  return this;
  6. }
  7.  
  8. #endregion
  9.  
  10. #region IEnumerator
  11.  
  12. public object Current
  13. {
  14.     get { return new object(); } // we're not accessing anything
  15. }
  16.  
  17. public bool MoveNext()
  18. {
  19.     return false; // never enter the foreach loop
  20. }
  21.  
  22. public void Reset()
  23. {
  24.     // do nothing
  25. }
  26.  
  27. #endregion
It still does the same thing! Only because you implemented that interface, regardless of the fact that MoveNext returns false, and the foreach loop is never entered!

To make it even stranger, you can completely remove AllControls list to see that it is doesn't make any difference - it's only the IEnumerator!

But, this will work, and will allow you to have a custom collection:

Expand|Select|Wrap|Line Numbers
  1. public IEnumerator GetEnumerator()
  2. {
  3.  return AllControls.GetEnumerator();
  4. }
  5.  
  6. // implementing IEnumerator is no longer needed
  7.  
Feb 6 '09 #6
tlhintoq
3,525 Recognized Expert Specialist
I am about to jump on a plane to Dallas for an installation. As soon as I get back next week I will play with this. Thank you very much for the code. I will pour over exactly how it works so that I can use it to just like the custom controls. I never like having code that works if I don't actually UNDERSTAND the why behind it. I'm guessing that I just made it more complicated than it needed to be; assuming I had to do more by hand than needed.
Feb 6 '09 #7
tlhintoq
3,525 Recognized Expert Specialist
That's my point exactly, your controls are removed from the Controls collection, not AllControls collection. Forms' controls are drawn only if they are part of the Controls collection.
I would still like to understand why and where they are being removed *at all*. I totally understand that the form can't display items if they aren't in the item's collection. But why or where is the enumeration process removing them from the collection?
Feb 6 '09 #8
Plater
7,872 Recognized Expert Expert
Perhaps your enumerator screws up the pointers to the collection object's natural pointer.
Like if your objects have internal enumeration pointers, and the collection object has one that points to those pointers....whe n you make your own enumeration it changes the pointers and the collection pointer now points to something that isn't there, it assumes it must be empty.

Like if you had a linked list with X amount of items, and removed the first item in the list, but didn't update the HEAD pointer first, the HEAD pointer would be looking at garbage and not at a list member anymore, so a traversal would appear empty?

That might not make sense to anyone else, but seems plausable to me.
Feb 6 '09 #9
vekipeki
229 Recognized Expert New Member
I just used ildasm to look at the IL for a small app, which called foreach on a control implementing IEnumerator.

And there was the answer: right after foreach ends, there is a check to see if the returned IEnumerator implements IDisposable, and if yes, it calls Dispose() to dispose it. To be sure, I added a breakpoint in the overriden Dispose(bool disposing) method and it was really called right after foreach.

So, in other words, CLR disposes the enumerator after use, but the enumerator is actually your control instance.

I guess this is why they always implement IEnumerator in a private class and instantiate on every call to GetEnumerator - so it can be collected immediately! :)
Feb 8 '09 #10

Sign in to post your reply or Sign up for a free account.

Similar topics

3
4543
by: Paul Porcelli | last post by:
I have the following code(excerpt) which grabs some lines from a syslog file and adds any found in the range to an array. @lines=();@vvlines=(); $t = new Net::Telnet (Timeout => 30, Prompt => '/<\d+\>/'); $t->open($t3); $t->login($username, $passwd); @lines=$t->cmd("tail -1000 syslog"); $t->close;
104
7207
by: cody | last post by:
What about an enhancement of foreach loops which allows a syntax like that: foeach(int i in 1..10) { } // forward foeach(int i in 99..2) { } // backwards foeach(char c in 'a'..'z') { } // chars foeach(Color c in Red..Blue) { } // using enums It should work with all integral datatypes. Maybe we can step a bit further: foeach(int i in 1..10, 30..100) { } // from 1 to 10 and 30 to hundred
32
6527
by: Joe Rattz | last post by:
Hmmm, I wrote the following code. I want an array of bools and I want to intialize them to false. bool bits = new bool; foreach(bool bit in bits) { bit = false; } The compiler complains on the "bit = false;" stating that bit is read-only.
15
2679
by: Mike Lansdaal | last post by:
I came across a reference on a web site (http://www.personalmicrocosms.com/html/dotnettips.html#richtextbox_lines ) that said to speed up access to a rich text box's lines that you needed to use a "foreach" loop instead of a "for" loop. This made absolutely no sense to me, but the author had posted his code and timing results. The "foreach" (a VB and other languages construct) was 0.01 seconds to access 1000 lines in rich text box,...
13
14500
by: TrintCSD | last post by:
How can I reset the collections within a foreach to be read as a change from within the foreach loop then restart the foreach after collections has been changed? foreach(string invoice in findListBox.listBox2.Items) { listBox2.Items count changed, restart this foreach } Thanks for any help.
8
3904
by: Dave Hagerich | last post by:
I'm using a DataGrid with a DataSet and I'm trying to filter the data being displayed, using the following code as a test: DataView theView = new DataView(theDataSet.Tables); theView.RowFilter = "'Record ID' = '0'"; theView.RowStateFilter = DataViewRowState.ModifiedCurrent; Debug.WriteLine(string.Format("RowFilter = {0}", theView.RowFilter)); RecordDataGrid.DataSource = theView; RecordDataGrid.DataBind();
4
2567
by: Sjoerd | last post by:
Summary: Use foreach(..) instead of while(list(..)=each(..)). --==-- Foreach is a language construct, meant for looping through arrays. There are two syntaxes; the second is a minor but useful extension of the first: foreach (array_expression as $value) statement
3
33373
by: Akira | last post by:
I noticed that using foreach is much slower than using for-loop, so I want to change our current code from foreach to for-loop. But I can't figure out how. Could someone help me please? Current code is here: foreach ( string propertyName in ht.Keys ) { this.setProperty( propertyName, ht );
2
3233
by: recordlovelife | last post by:
So I am trying to display a title, date, and content of a wordpress blog. Word press provides nice drop in functions to get the job done with simple names like "the_title", and the "the_content" But on the homepage of a site, i wanted to truncate the content to like the first 75 characters and then put "..." (a perfect use of the smarty "truncate" modifier) and then give the visitor a link to read the whole article. But since "the_content" is a...
0
9707
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9585
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
10323
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10082
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9161
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7622
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5658
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4301
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3823
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.