Перечислять элементы управления.Net в общем (MenuStrip, ToolStrip, StatusStrip)
У меня есть код, который в общем случае получает все элементы управления в форме и помещает их в список. Вот часть кода:
private List<Control> GetControlList(Form parentForm)
{
List<Control> controlList = new List<Control>();
AddControlsToList(parentForm.Controls, controlList);
return controlList;
}
private void AddControlsToList(Control.ControlCollection rootControls, List<Control> controlList)
{
foreach (Control c in rootControls)
{
controlList.Add(c);
if (c.HasChildren)
AddControlsToList(c.Controls, controlList);
//
}
}
Поэтому я могу использовать только c.HasChildren, чтобы проверить, есть ли еще дочерние элементы управления от этого корневого элемента управления.
А как насчет menuStrip, toolStrip и statusStrip? Как я могу получить все элементы управления, которые находятся в этих элементах управления в целом? Пример: MenuStripItem
Я знаю, что я мог бы попробовать протестировать c.GetType() == typeof(MenuStrip), но я надеялся, что мне не придется делать тесты определенного типа.
Если мне нужно дать больше информации, пожалуйста, спросите.
Огромное спасибо
3 ответа
Я считаю, что конструктор VS делает это, получая экземпляр конструктора элемента управления (см. Designer
атрибут), и, если дизайнер ComponentDesigner
, получая AssociatedComponents
имущество.
РЕДАКТИРОВАТЬ:
Хорошо, я думаю, это немного расплывчато. Предупреждение, однако: то, что следует, немного сложно, и может не стоить усилий.
Примечание по номенклатуре:
Ниже я буду ссылаться как на дизайнера в Visual Studio - это имя используется для обозначения функциональности в Visual Studio, с помощью которой макет и содержимое форм и элементов управления редактируются визуально, - так и на классы дизайнера, - которые будут объяснены ниже. ниже. Чтобы избежать путаницы в отношении того, к чему я обращаюсь в любой момент времени, я всегда буду ссылаться на функциональность дизайнера в Visual Studio как на "конструктор", и я всегда буду ссылаться на класс конструктора как "IDesigner", который является Интерфейс каждый должен реализовать.
Когда дизайнер Visual Studio загружает компонент (обычно элемент управления, но также и такие вещи, как Timer
и такое), он ищет пользовательский атрибут в классе типа DesignerAttribute
, (Те, кто не знаком с атрибутами, могут захотеть прочитать их, прежде чем продолжить.)
Этот атрибут, если он присутствует, предоставляет имя класса - IDesigner - дизайнер может использовать для взаимодействия с компонентом. По сути, этот класс контролирует определенные аспекты конструктора и поведение компонента во время разработки. На самом деле вы можете многое сделать с IDesigner, но сейчас нас интересует только одно.
Большинство элементов управления, которые используют пользовательский IDesigner, используют тот, который происходит от ControlDesigner
что само по себе вытекает из ComponentDesigner
, ComponentDesigner
класс имеет публичное виртуальное свойство AssociatedComponents
, которая предназначена для переопределения в производных классах, чтобы возвращать коллекцию ссылок на все "дочерние" компоненты этого.
Чтобы быть более конкретным, ToolStrip
контроль (и по наследству, MenuStrip
контроль) имеет DesignerAttribute
который ссылается на класс под названием ToolStripDesigner
, Это выглядит примерно так:
/*
* note that in C#, I can refer to the "DesignerAttribute" class within the [ brackets ]
* by simply "Designer". The compiler adds the "Attribute" to the end for us (assuming
* there's no attribute class named simply "Designer").
*/
[Designer("System.Windows.Forms.Design.ToolStripDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), ...(other attributes)]
public class ToolStrip : ScrollableControl, IArrangedElement, ...(other interfaces){
...
}
ToolStripDesigner
класс не публичный. Это внутреннее в System.Design.dll. Но так как он указан здесь полностью определенным именем, конструктор VS может использовать Activator.CreateInstance
создать экземпляр этого в любом случае.
это ToolStripDesigner
класс, потому что он наследует [косвенно] от ComponentDesigner
имеет AssociatedComponents
имущество. Когда вы звоните, вы получаете новый ArrayList
который содержит ссылки на все элементы, которые были добавлены в ToolStrip
,
Так как должен выглядеть ваш код, чтобы делать то же самое? Довольно запутанный, но я думаю, что у меня есть рабочий пример:
/*
* Some controls will require that we set their "Site" property before
* we associate a IDesigner with them. This "site" is used by the
* IDesigner to get services from the designer. Because we're not
* implementing a real designer, we'll create a dummy site that
* provides bare minimum services and which relies on the framework
* for as much of its functionality as possible.
*/
class DummySite : ISite, IDisposable{
DesignSurface designSurface;
IComponent component;
string name;
public IComponent Component {get{return component;}}
public IContainer Container {get{return designSurface.ComponentContainer;}}
public bool DesignMode{get{return false;}}
public string Name {get{return name;}set{name = value;}}
public DummySite(IComponent component){
this.component = component;
designSurface = new DesignSurface();
}
~DummySite(){Dispose(false);}
protected virtual void Dispose(bool isDisposing){
if(isDisposing)
designSurface.Dispose();
}
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
public object GetService(Type serviceType){return designSurface.GetService(serviceType);}
}
static void GetComponents(IComponent component, int level, Action<IComponent, int> action){
action(component, level);
bool visible, enabled;
Control control = component as Control;
if(control != null){
/*
* Attaching the IDesigner sets the Visible and Enabled properties to true.
* This is useful when you're designing your form in Visual Studio, but at
* runtime, we'd rather the controls maintain their state, so we'll save the
* values of these properties and restore them after we detach the IDesigner.
*/
visible = control.Visible;
enabled = control.Enabled;
foreach(Control child in control.Controls)
GetComponents(child, level + 1, action);
}else visible = enabled = false;
/*
* The TypeDescriptor class has a handy static method that gets
* the DesignerAttribute of the type of the component we pass it
* and creates an instance of the IDesigner class for us. This
* saves us a lot of trouble.
*/
ComponentDesigner des = TypeDescriptor.CreateDesigner(component, typeof(IDesigner)) as ComponentDesigner;
if(des != null)
try{
DummySite site;
if(component.Site == null)
component.Site = site = new DummySite(component);
else site = null;
try{
des.Initialize(component);
foreach(IComponent child in des.AssociatedComponents)
GetComponents(child, level + 1, action);
}finally{
if(site != null){
component.Site = null;
site.Dispose();
}
}
}finally{des.Dispose();}
if(control != null){
control.Visible = visible;
control.Enabled = enabled;
}
}
/* We'll use this in the ListComponents call */
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
const int WM_SETREDRAW = 11;
void ListComponents(){
/*
* Invisible controls and disabled controls will be temporarily shown and enabled
* during the GetComponents call (see the comment within that call), so to keep
* them from showing up and then disappearing again (or appearing to temporarily
* change enabled state), we'll disable redrawing of our window and re-enable it
* afterwards.
*/
SendMessage(Handle, WM_SETREDRAW, 0, 0);
GetComponents(this, 0,
/* You'll want to do something more useful here */
(component, level)=>System.Diagnostics.Debug.WriteLine(new string('\t', level) + component));
SendMessage(Handle, WM_SETREDRAW, 1, 0);
}
Такие элементы, как ToolStripItem и т. Д., На самом деле не являются элементами управления, это просто компоненты, которые составляют ToolStrip или MenuStrip.
Это означает, что если вы хотите включить эти компоненты в свой плоский список элементов управления, вам нужно будет выполнить определенные проверки.
ToolStripControlHost может содержать элемент управления:
if (c is ToolStrip)
foreach (ToolStripItem item in EnumerateTree(c, "Items"))
if (item is ToolStripControlHost)
AddControlsToList(
new Control[] { ((ToolStripControlHost)item).Control },
controlList);
... это если вы измените аргумент 1 на тип IEnumerable<Control>
и написать свою собственную функцию EnumerateTree (я думаю, что здорово иметь один хороший универсальный метод EnumerateTree).