Перечислять элементы управления .Net в общем (MenuStrip, ToolStrip, StatusStrip) - PullRequest
2 голосов
/ 18 ноября 2008

У меня есть код, который в общем случае получает все элементы управления в форме и помещает их в список. Вот часть кода:

        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 ]

6 голосов
/ 18 ноября 2008

Я считаю, что конструктор VS делает это, получая экземпляр конструктора элемента управления (см. Атрибут Designer ), и, если конструктор является ComponentDesigner, получение свойства AssociatedComponents.

EDIT

Хорошо, я думаю, это немного расплывчато. Однако предупреждение: то, что следует, немного сложнее и может не стоить усилий.

Примечание по номенклатуре:
Ниже я буду ссылаться как на дизайнера в 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);
}
0 голосов
/ 18 ноября 2008

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).

0 голосов
/ 18 ноября 2008

Такие элементы, как ToolStripItem и т. Д., На самом деле не являются элементами управления, это просто компоненты, которые составляют ToolStrip или MenuStrip.

Это означает, что если вы хотите включить эти компоненты в свой плоский список элементов управления, вам нужно будет выполнить определенные проверки.

...