74

I have a ToolStripMenuItem called myMenu. How can I access this like so:

/* Normally, I would do: */
this.myMenu... etc.

/* But how do I access it like this: */
String name = myMenu;
this.name...

This is because I am dynamically generating ToolStripMenuItems from an XML file and need to reference MenuItems by their dynamically generated names.

14 Answers14

138

Use the Control.ControlCollection.Find method.

Try this:

this.Controls.Find()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284
  • 3
    http://msdn.microsoft.com/en-us/library/system.windows.forms.control.controlcollection.find%28VS.80%29.aspx – RvdK Oct 08 '09 at 09:55
  • 2
    This doesn't work for me. I think because, as o3o has pointed out, a ToolStripMenuItem is not a Control. – Luca Nov 28 '10 at 17:27
  • 25
    For a textbox, I had to cast to the control type and take the first element like this: `((TextBox) frm.Controls.Find("controlName",true)[0]).Text = "yay";` – Dan W Apr 16 '12 at 01:05
  • @DanW this is so much like Java – mrid Dec 18 '17 at 09:04
  • What if I have a part of the name and I need a list of controls who have that string in their name? – Oiproks May 27 '20 at 14:52
51
string name = "the_name_you_know";

Control ctn = this.Controls[name];

ctn.Text = "Example...";
Bob Kaufman
  • 12,864
  • 16
  • 78
  • 107
vaNIts
  • 519
  • 3
  • 2
11

Assuming you have the menuStrip object and the menu is only one level deep, use:

ToolStripMenuItem item = menuStrip.Items
    .OfType<ToolStripMenuItem>()
    .SelectMany(it => it.DropDownItems.OfType<ToolStripMenuItem>())
    .SingleOrDefault(n => n.Name == "MyMenu");

For deeper menu levels add more SelectMany operators in the statement.

if you want to search all menu items in the strip then use

ToolStripMenuItem item = menuStrip.Items
    .Find("MyMenu",true)
    .OfType<ToolStripMenuItem>()
    .Single();

However, make sure each menu has a different name to avoid exception thrown by key duplicates.

To avoid exceptions you could use FirstOrDefault instead of SingleOrDefault / Single, or just return a sequence if you might have Name duplicates.

nalply
  • 26,770
  • 15
  • 78
  • 101
dmihailescu
  • 1,625
  • 17
  • 15
  • 1
    This should be the right solution. Other people did not know that ToolStripMenuItem is not control. Due to this reason their solutions do not work for ToolStripMenuItem. – Ales100 Jun 29 '22 at 06:22
8
Control GetControlByName(string Name)
{
    foreach(Control c in this.Controls)
        if(c.Name == Name)
            return c;

    return null;
}

Disregard this, I reinvent wheels.

  • More of a general-purpose-ish solution than Julien's. Still works fine though. –  Oct 08 '09 at 09:57
  • this is not reinventing the wheel, it's a good solution that can be easily adapted to the case where you don't know a control's name or want to view the names of each control or do many things accessing controls, and was thus useful to me. – barlop Apr 20 '16 at 08:17
5

Using the same approach of Philip Wallace, we can do like this:

    public Control GetControlByName(Control ParentCntl, string NameToSearch)
    {
        if (ParentCntl.Name == NameToSearch)
            return ParentCntl;

        foreach (Control ChildCntl in ParentCntl.Controls)
        {
            Control ResultCntl = GetControlByName(ChildCntl, NameToSearch);
            if (ResultCntl != null)
                return ResultCntl;
        }
        return null;
    }

Example:

    public void doSomething() 
    {
            TextBox myTextBox = (TextBox) this.GetControlByName(this, "mytextboxname");
            myTextBox.Text = "Hello!";
    }

I hope it help! :)

Community
  • 1
  • 1
oviniciusfeitosa
  • 915
  • 1
  • 11
  • 12
4

this.Controls.Find(name, searchAllChildren) doesn't find ToolStripItem because ToolStripItem is not a Control

  using SWF = System.Windows.Forms;
  using NUF = NUnit.Framework;
  namespace workshop.findControlTest {
     [NUF.TestFixture]
     public class FormTest {
        [NUF.Test]public void Find_menu() {
           // == prepare ==
           var fileTool = new SWF.ToolStripMenuItem();
           fileTool.Name = "fileTool";
           fileTool.Text = "File";

           var menuStrip = new SWF.MenuStrip();
           menuStrip.Items.Add(fileTool);

           var form = new SWF.Form();
           form.Controls.Add(menuStrip);

           // == execute ==
           var ctrl = form.Controls.Find("fileTool", true);

           // == not found! ==
           NUF.Assert.That(ctrl.Length, NUF.Is.EqualTo(0)); 
        }
     }
  }
o3o
  • 1,094
  • 13
  • 23
4

One of the best way is a single row of code like this:

In this example we search all PictureBox by name in a form

PictureBox[] picSample = 
                    (PictureBox)this.Controls.Find(PIC_SAMPLE_NAME, true);

Most important is the second paramenter of find.

if you are certain that the control name exists you can directly use it:

  PictureBox picSample = 
                        (PictureBox)this.Controls.Find(PIC_SAMPLE_NAME, true)[0];
daniele3004
  • 13,072
  • 12
  • 67
  • 75
4

You can use find function in your Form class. If you want to cast (Label) ,(TextView) ... etc, in this way you can use special features of objects. It will be return Label object.

(Label)this.Controls.Find(name,true)[0];

name: item name of searched item in the form

true: Search all Children boolean value

Ferhat KOÇER
  • 3,890
  • 1
  • 26
  • 26
3
this.Controls["name"];

This is the actual code that is ran:

public virtual Control this[string key]
{
    get
    {
        if (!string.IsNullOrEmpty(key))
        {
            int index = this.IndexOfKey(key);
            if (this.IsValidIndex(index))
            {
                return this[index];
            }
        }
        return null;
    }
}

vs:

public Control[] Find(string key, bool searchAllChildren)
{
    if (string.IsNullOrEmpty(key))
    {
        throw new ArgumentNullException("key", SR.GetString("FindKeyMayNotBeEmptyOrNull"));
    }
    ArrayList list = this.FindInternal(key, searchAllChildren, this, new ArrayList());
    Control[] array = new Control[list.Count];
    list.CopyTo(array, 0);
    return array;
}

private ArrayList FindInternal(string key, bool searchAllChildren, Control.ControlCollection controlsToLookIn, ArrayList foundControls)
{
    if ((controlsToLookIn == null) || (foundControls == null))
    {
        return null;
    }
    try
    {
        for (int i = 0; i < controlsToLookIn.Count; i++)
        {
            if ((controlsToLookIn[i] != null) && WindowsFormsUtils.SafeCompareStrings(controlsToLookIn[i].Name, key, true))
            {
                foundControls.Add(controlsToLookIn[i]);
            }
        }
        if (!searchAllChildren)
        {
            return foundControls;
        }
        for (int j = 0; j < controlsToLookIn.Count; j++)
        {
            if (((controlsToLookIn[j] != null) && (controlsToLookIn[j].Controls != null)) && (controlsToLookIn[j].Controls.Count > 0))
            {
                foundControls = this.FindInternal(key, searchAllChildren, controlsToLookIn[j].Controls, foundControls);
            }
        }
    }
    catch (Exception exception)
    {
        if (ClientUtils.IsSecurityOrCriticalException(exception))
        {
            throw;
        }
    }
    return foundControls;
}
Philip Wallace
  • 7,905
  • 3
  • 28
  • 40
3

Assuming you have Windows.Form Form1 as the parent form which owns the menu you've created. One of the form's attributes is named .Menu. If the menu was created programmatically, it should be the same, and it would be recognized as a menu and placed in the Menu attribute of the Form.

In this case, I had a main menu called File. A sub menu, called a MenuItem under File contained the tag Open and was named menu_File_Open. The following worked. Assuming you

// So you don't have to fully reference the objects.
using System.Windows.Forms;

// More stuff before the real code line, but irrelevant to this discussion.

MenuItem my_menuItem = (MenuItem)Form1.Menu.MenuItems["menu_File_Open"];

// Now you can do what you like with my_menuItem;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
shooky56
  • 31
  • 1
2

Since you're generating them dynamically, keep a map between a string and the menu item, that will allow fast retrieval.

// in class scope
private readonly Dictionary<string, ToolStripMenuItem> _menuItemsByName = new Dictionary<string, ToolStripMenuItem>();

// in your method creating items
ToolStripMenuItem createdItem = ...
_menuItemsByName.Add("<name here>", createdItem);

// to access it
ToolStripMenuItem menuItem = _menuItemsByName["<name here>"];
Julien Lebosquain
  • 40,639
  • 8
  • 105
  • 117
  • 1
    Now THERE'S an idea! +1 (although only works if the control is already in the dictionary) –  Oct 08 '09 at 09:55
0

Have a look at the ToolStrip.Items collection. It even has a find method available.

Neil
  • 1,301
  • 14
  • 27
0

You can do the following:

private ToolStripMenuItem getToolStripMenuItemByName(string nameParam)
   {
      foreach (Control ctn in this.Controls)
         {
            if (ctn is ToolStripMenuItem)
               {
                   if (ctn.Name = nameParam)
                      {
                         return ctn;
                      }
                }
         }
         return null;
    }
o3o
  • 1,094
  • 13
  • 23
Koekiebox
  • 5,793
  • 14
  • 53
  • 88
0

A simple solution would be to iterate through the Controls list in a foreach loop. Something like this:

foreach (Control child in Controls)
{
    // Code that executes for each control.
}

So now you have your iterator, child, which is of type Control. Now do what you will with that, personally I found this in a project I did a while ago in which it added an event for this control, like this:

child.MouseDown += new MouseEventHandler(dragDown);
carefulnow1
  • 803
  • 12
  • 30