Ответ 1
Используйте метод Control.ControlCollection.Find.
Попробуйте следующее:
this.Controls.Find()
У меня есть ToolStripMenuItem, называемый "myMenu". Как я могу получить доступ к этому так:
/* Normally, I would do: */
this.myMenu... etc.
/* But how do I access it like this: */
String name = myMenu;
this.name...
Это связано с тем, что я динамически генерирую ToolStripMenuItems из файла XML и нуждаюсь в ссылке на элементы меню их динамически сгенерированными именами.
Используйте метод Control.ControlCollection.Find.
Попробуйте следующее:
this.Controls.Find()
string name = "the_name_you_know";
Control ctn = this.Controls[name];
ctn.Text = "Example...";
Control GetControlByName(string Name)
{
foreach(Control c in this.Controls)
if(c.Name == Name)
return c;
return null;
}
Не обращайте на это внимания, я изобретаю колеса.
this.Controls.Find(name, searchAllChildren) не находит ToolStripItem, потому что ToolStripItem не является элементом управления
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));
}
}
}
Предполагая, что у вас есть объект menuStrip
, и меню находится только на одном уровне, используйте:
ToolStripMenuItem item = menuStrip.Items
.OfType<ToolStripMenuItem>()
.SelectMany(it => it.DropDownItems.OfType<ToolStripMenuItem>())
.SingleOrDefault(n => n.Name == "MyMenu");
Для более глубоких уровней меню добавьте еще несколько операторов SelectMany в оператор.
если вы хотите искать все пункты меню в полосе, используйте
ToolStripMenuItem item = menuStrip.Items
.Find("MyMenu",true)
.OfType<ToolStripMenuItem>()
.Single();
Однако убедитесь, что у каждого меню есть другое имя, чтобы избежать исключения, вызванного дубликатами ключа.
Чтобы избежать исключений, вы можете использовать FirstOrDefault
вместо SingleOrDefault
/Single
или просто вернуть последовательность, если у вас может быть дубликат Name
.
Поскольку вы генерируете их динамически, сохраняйте карту между строкой и пунктом меню, что позволит быстро найти.
// 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>"];
this.Controls["name"];
Это фактический код, который выполняется:
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;
}
}
против
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;
}
Предполагая, что у вас есть Windows.Form Form1
в качестве родительской формы, которая владеет созданным вами меню. Один из атрибутов формы называется .Menu
. Если меню было создано программно, оно должно быть одинаковым, и оно будет распознано как меню и помещено в атрибут Menu формы.
В этом случае у меня было главное меню File
. Подменю, называемое MenuItem
под File
, содержало тег Open
и было названо menu_File_Open
. Работало. Предполагая, что вы
// 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;
Это может помочь вам:
public Control RecursiveFind(Control ParentCntl, string NameToSearch)
{
if (ParentCntl.ID == NameToSearch)
return ParentCntl;
foreach (Control ChildCntl in ParentCntl.Controls)
{
Control ResultCntl = RecursiveFind(ChildCntl , NameToSearch);
if (ResultCntl != null)
return ResultCntl;
}
return null;
}
myForm.FindControl<Label>("myLabel");
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MyExtensions
{
public static class Extensions
{
public static IEnumerable<T> FindControl<T>(this Control parentControl, String name = "")
{
if (parentControl is T)
{
if (String.IsNullOrWhiteSpace(name))
yield return (T)(object)parentControl;
else if (parentControl.Name.Equals(name))
{
yield return (T)(object)parentControl;
yield break;
}
}
var filteredControlList = from controlList in parentControl.Controls.Cast<Control>()
where controlList is T || controlList.Controls.Count > 0
select controlList;
foreach (Control childControl in filteredControlList)
{
foreach(T foundControl in FindControl<T>(childControl, name))
{
yield return foundControl;
if (!String.IsNullOrWhiteSpace(name))
yield break;
}
}
}
}
}
Используя тот же подход Philip Wallace, мы можем сделать вот так:
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;
}
Пример:
public void doSomething()
{
TextBox myTextBox = (TextBox) this.GetControlByName(this, "mytextboxname");
myTextBox.Text = "Hello!";
}
Я надеюсь, что это поможет!:)
Посмотрите на коллекцию ToolStrip.Items. Он даже имеет доступный метод поиска.
Вы можете сделать следующее:
private ToolStripMenuItem getToolStripMenuItemByName(string nameParam) { foreach (Control ctn in this.Controls) { if (ctn is ToolStripMenuItem) { if (ctn.Name = nameParam) { return ctn; } } } return null; }
Простым решением будет итерация через список Controls
в цикле foreach
. Что-то вроде этого:
foreach (Control child in Controls)
{
// Code that executes for each control.
}
Итак, теперь у вас есть итератор, child
, который имеет тип Control
. Теперь сделайте все, что захотите, лично я нашел это в проекте, который я сделал некоторое время назад, в котором он добавил событие для этого элемента управления, например:
child.MouseDown += new MouseEventHandler(dragDown);
Один из лучших способов - это одна строка кода:
В этом примере мы ищем все PictureBox
по имени в форме
PictureBox[] picSample =
(PictureBox)this.Controls.Find(PIC_SAMPLE_NAME, true);
Наиболее важным является второй параграф find
.
если вы уверены, что имя элемента управления существует, вы можете напрямую его использовать:
PictureBox picSample =
(PictureBox)this.Controls.Find(PIC_SAMPLE_NAME, true)[0];