views:

917

answers:

6

I have a main form (frmMain), with some buttons (button1, button2...).

then I do this:

object objectsContainer = frmMain; // <--now the object contains form base, button1, button2...

how could I loop through all containing items in my object to access butto1, button2...???

I did this, but it's not what I want.

foreach (PropertyInfo pInfo in objectsContainer.GetType().GetProperties())     
{

}

I want to do something like this:

foreach (object objectFromForm in objectsContainer)     // <--- how to do this looping an object (type of form)
{
    //here is objectFromForm = base, button1, button2...
    foreach (PropertyInfo pInfo in objectFromForm .GetType().GetProperties())     
    {
               //here it's possible to access pInfo and its properties like size, name ...
    }
}

When I'm debuggin and looking at the content of objectsContainer there are all "infos" that I want.

Some suggestions??

Best regards.

**

UPDATE:

**

OK, I made a test project. There you could see what I want to do. In the project is an image with the objects... Here you can download it: http://www.mediafire.com/download.php?ik5j3ejnzm2

Best regards.

+2  A: 

Why don't you just use the Controls property of the form?

foreach(var control in form.Controls)
{
     // Do something with the thing
}
Martinho Fernandes
Because: form.Controls returns only a toolstrip control but not the others inside of toolstrip.
Jooj
+13  A: 

You can loop the ControlCollection.

Just remember that these controls can be nested, if they are in panels eg.

private void RecusiceControls(ControlCollection controls)
        {
            foreach (Control control in controls)
            {
                RecusiceControls((ControlCollection)control.Controls);
                if (control is Button)
                {
                }
            }
        }

Have a look at this

Find a control recursively in C#

astander
+19  A: 

Each Control has a Controls collection that you can iterate through to get the full hierarchy, but unfortunately ToolStrip items use a different object model (they aren't all Controls); as such, you can iterate such a setup (to include the menu items too), but it isn't trivial; here's an example:

    IEnumerable RecurseObjects(object root) {
        Queue items = new Queue();
        items.Enqueue(root);
        while (items.Count > 0) {
            object obj = items.Dequeue();
            yield return obj;
            Control control = obj as Control;
            if (control != null) {
                // regular controls and sub-controls
                foreach (Control item in control.Controls) {
                    items.Enqueue(item);
                }
                // top-level menu items
                ToolStrip ts = control as ToolStrip;
                if (ts != null) {
                    foreach(ToolStripItem tsi in ts.Items) {
                        items.Enqueue(tsi);
                    }
                }
            }
            // child menus
            ToolStripDropDownItem tsddi = obj as ToolStripDropDownItem;
            if (tsddi != null && tsddi.HasDropDownItems) {
                foreach (ToolStripItem item in tsddi.DropDownItems) {
                    items.Enqueue(item);
                }
            }
        }            
    }

You might call this, for example, via something like:

    foreach (object obj in RecurseObjects(this)) {
        Console.WriteLine(obj);
    }

Of course, then the question is: what do you want to do with each item?

Marc Gravell
Impressive use of the queue.
Gregory
Avoids taking too much stack space ;-p
Marc Gravell
yield return seems better
Behrooz
@behrooz - it **does** use `yield return`; what it *doesn't* do is self-recurse on the stack to get child-controls, which can actually be very wasteful.
Marc Gravell
+1  A: 
public IEnumerable<Control> Get (object o)  
{  
    if (o is System.Windows.Forms.Control)  
    {  
        System.Windows.Forms.Control f =(System.Windows.Forms.Control)o;  
        foreach(System.Windows.Forms.Control c in f.Controls)  
        {  
            yield return c;
            foreach(System.Windows.Forms.Control c2 in Get(c))  
            {
                yield return c2;
            }
        }  
    }  
}
Behrooz
Here you do `is`, but then cast, but as `Control` is a reference type it's quicker to do `var f = o as Control; if( f != null ) ...`
Keith
I just wanted to use the "yield return" statement and don't use a "Queue".
Behrooz
Hello Keith, I've tried your solution but got an error: foreach statement cannot operate on variables of type 'System.Collections.Generic.IEnumerator<System.Windows.Forms.Control>' because 'System.Collections.Generic.IEnumerator<System.Windows.Forms.Control>' does not contain a public definition for 'GetEnumerator'
Jooj
The return type should be `IEnumerable<Control>`, not `IEnumerator<Control>`
Keith
Ok. I Wrote it @ 2AM.I had no gray cell left in my brain.
Behrooz
A: 

How about this (similar to other answers but simple and easy to use):

using System.Windows.Forms;

public static class ControlExtensions 
{
    public IEnumerable<Control> DescendantControls(this Control control)  
    {   
        foreach(var child in control.Controls)  
        {  
            yield return child;
            foreach(var descendant in child.DescendantControls())  
            {
                yield return descendant;
            }
        }
    }
}

I've got a utility function like this that I use quite often.

Keith
How to use this code? I've added it in the main form and got an error:Extension methods must be defined in a non-generic static class
Jooj
Updated example - this needs to be in its own static class. The you will be able to do `foreach(var ctrl in frmMain.DescendantControls() ) { ...`
Keith
A: 

I decided to give this a try. I downloaded your sample app and modified your main form to include the algorithm I think you have asked for. Hope this helps:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Test
{
   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();

         //if you debug this code then the objects is holding all controls on this form
         object objects = this;

         Dictionary<Control, string> allControls = GetIt(objects);
      }

      /// <summary>
      /// How to get all control names an .Text values if availible (included this form)???
      /// </summary>
      private Dictionary<Control, string> GetIt(object objects)
      {
          Dictionary<Control, string> found = new Dictionary<Control, string>();
          Queue<Control> controlQueue = new Queue<Control>();
          controlQueue.Enqueue(objects as Control);

          while (controlQueue.Count > 0)
          {
              Control item = controlQueue.Dequeue();
              foreach (Control ctrl in item.Controls)
              {
                  controlQueue.Enqueue(ctrl);
              }
              found.Add(item, item.Text);
          }

          return found;
      }
   }
}
Mark Ewer
Thanks Mark. Near to perfect just a little thing... how to get all MenuItems like menu22ToolStripMenuItem = "Menu2.2" and other subitems like text1ToolStripMenuItem = "text1" and the combobox text items?Regards.
Jooj
ummm. I guess you would need reflection to find collections of controls. I'll take another whack at it.
Mark Ewer