一般枚举.Net控件的项目(MenuStrip,ToolStrip,StatusStrip)

时间:2021-03-17 21:05:02

I've got some code that will generically get all Controls in a form and put them in a list. Here's some of the code:

我有一些代码,通常会在表单中获取所有控件并将它们放在列表中。这是一些代码:

        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);
                //
            }
        }

So I'm only able to use c.HasChildren to check and see if there's any more child controls from this root control.

所以我只能使用c.HasChildren检查并查看是否还有来自此根控件的子控件。

What about a menuStrip, toolStrip, and statusStrip? How do I get all of the controls that are in these controls generically? Ex: MenuStripItem

menuStrip,toolStrip和statusStrip怎么样?如何获得这些控件中的所有控件?例如:MenuStripItem

I know that I could try testing the c.GetType() == typeof(MenuStrip) but I was hoping to not have to do specific type tests.

我知道我可以尝试测试c.GetType()== typeof(MenuStrip),但我希望不必进行特定的类型测试。

If I need to give more info, please ask.

如果我需要提供更多信息,请询问。

Thanks a bunch

谢谢你们

3 个解决方案

#1


6  

I believe the VS designer does it by getting an instance of the control's designer (see the Designer attribute), and, if the designer is a ComponentDesigner, getting the AssociatedComponents property.

我相信VS设计师通过获取控件设计器的实例(参见Designer属性)来实现它,如果设计器是ComponentDesigner,则获取AssociatedComponents属性。

EDIT:

Okay, I guess that's a little vague. A warning, though: what follows is a little complicated, and might not be worth the effort.

好吧,我猜这有点模糊。但是有一个警告:接下来的内容有点复杂,可能不值得付出努力。

A note on nomenclature:
Below, I will be referring to both the designer within Visual Studio—which is the name used to refer to the functionality within Visual Studio by which the layout and content of forms and controls are edited visually—and to designer classes—which will be explained below. To prevent confusion as to which I am referring to at any given time, I will always refer to the designer functionality within Visual Studio as "the designer", and I will always refer to a designer class as an "IDesigner", which is the interface each must implement.

关于命名法的注释:下面,我将指代Visual Studio中的设计器 - 这是用于引用Visual Studio中的功能的名称,通过它可以直观地编辑表单和控件的布局和内容,以及设计器类 - 这将在下面解释。为了防止在任何给定时间引起混淆,我将始终将Visual Studio中的设计器功能称为“设计器”,并且我将始终将设计器类称为“IDesigner”,这是每个界面都必须实现。

When the Visual Studio designer loads a component (usually a control, but also things like Timer and such), it looks for a custom attribute on the class of type DesignerAttribute. (Those unfamiliar with attributes might want read up on them before continuing.)

当Visual Studio设计器加载一个组件(通常是一个控件,还有像Timer这样的东西)时,它会在类型为DesignerAttribute的类上查找自定义属性。 (那些不熟悉属性的人可能希望在继续之前阅读它们。)

This attribute, if present, provides the name of a class—an IDesigner—the designer can use to interface with the component. In effect, this class controls certain aspects of the designer and of the design-time behavior of the component. There's indeed quite a lot you can do with an IDesigner, but right now we're only interested in one thing.

此属性(如果存在)提供类的名称 - 一个IDesigner - 设计人员可以使用该名称与组件进行交互。实际上,此类控制设计器的某些方面以及组件的设计时行为。你可以用IDesigner做很多事情,但是现在我们只对一件事情感兴趣。

Most controls that use a custom IDesigner use one that derives from ControlDesigner, which itself derives from ComponentDesigner. The ComponentDesigner class has a public virtual property called AssociatedComponents, which is meant to be overridden in derived classes to return a collection of references to all "child" components of this one.

大多数使用自定义IDesigner的控件都使用从ControlDesigner派生的控件,ControlDesigner本身派生自ComponentDesigner。 ComponentDesigner类具有一个名为AssociatedComponents的公共虚拟属性,该属性旨在在派生类中重写,以返回对此节点的所有“子”组件的引用集合。

To be more specific, the ToolStrip control (and by inheritance, the MenuStrip control) has a DesignerAttribute that references a class called ToolStripDesigner. It looks sort of like:

更具体地说,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){
    ...
}

The ToolStripDesigner class is not public. It's internal to System.Design.dll. But since it's specified here by it's fully qualified name, the VS designer can use Activator.CreateInstance to create an instance of it anyway.

ToolStripDesigner类不公开。它是System.Design.dll的内部。但由于它是由完全限定名称指定的,因此VS设计者可以使用Activator.CreateInstance来创建它的实例。

This ToolStripDesigner class, because it inherits [indirectly] from ComponentDesigner has an AssociatedComponents property. When you call it you get a new ArrayList that contains references to all the items that have been added to the ToolStrip.

这个ToolStripDesigner类,因为它从ComponentDesigner继承[间接]具有AssociatedComponents属性。当您调用它时,您将获得一个新的ArrayList,其中包含对已添加到ToolStrip的所有项的引用。

So what would your code have to look like to do the same thing? Rather convoluted, but I think I have a working example:

那么你的代码看起来像做同样的事情是什么?相当令人费解,但我想我有一个有效的例子:

/*
 * 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);
}

#2


0  

The items such as ToolStripItem etc aren't actually controls, they are simply components that make up a ToolStrip or MenuStrip.

ToolStripItem等项目实际上不是控件,它们只是组成ToolStrip或MenuStrip的组件。

Which means, that if you want to include those components in your flattened list of controls then you will need to do the specific checks.

这意味着,如果要将这些组件包含在展平的控件列表中,则需要进行特定检查。

#3


0  

ToolStripControlHost might contain a Control:

ToolStripControlHost可能包含一个Control:

if (c is ToolStrip)
    foreach (ToolStripItem item in EnumerateTree(c, "Items"))
        if (item is ToolStripControlHost)
            AddControlsToList(
                new Control[] { ((ToolStripControlHost)item).Control },
                controlList);

...that's if you change argument 1 to type IEnumerable<Control> and write your own EnumerateTree function (I think it's great to have one good generic EnumerateTree method).

...如果您更改参数1以键入IEnumerable 并编写您自己的EnumerateTree函数(我认为拥有一个良好的通用EnumerateTree方法很棒)。

#1


6  

I believe the VS designer does it by getting an instance of the control's designer (see the Designer attribute), and, if the designer is a ComponentDesigner, getting the AssociatedComponents property.

我相信VS设计师通过获取控件设计器的实例(参见Designer属性)来实现它,如果设计器是ComponentDesigner,则获取AssociatedComponents属性。

EDIT:

Okay, I guess that's a little vague. A warning, though: what follows is a little complicated, and might not be worth the effort.

好吧,我猜这有点模糊。但是有一个警告:接下来的内容有点复杂,可能不值得付出努力。

A note on nomenclature:
Below, I will be referring to both the designer within Visual Studio—which is the name used to refer to the functionality within Visual Studio by which the layout and content of forms and controls are edited visually—and to designer classes—which will be explained below. To prevent confusion as to which I am referring to at any given time, I will always refer to the designer functionality within Visual Studio as "the designer", and I will always refer to a designer class as an "IDesigner", which is the interface each must implement.

关于命名法的注释:下面,我将指代Visual Studio中的设计器 - 这是用于引用Visual Studio中的功能的名称,通过它可以直观地编辑表单和控件的布局和内容,以及设计器类 - 这将在下面解释。为了防止在任何给定时间引起混淆,我将始终将Visual Studio中的设计器功能称为“设计器”,并且我将始终将设计器类称为“IDesigner”,这是每个界面都必须实现。

When the Visual Studio designer loads a component (usually a control, but also things like Timer and such), it looks for a custom attribute on the class of type DesignerAttribute. (Those unfamiliar with attributes might want read up on them before continuing.)

当Visual Studio设计器加载一个组件(通常是一个控件,还有像Timer这样的东西)时,它会在类型为DesignerAttribute的类上查找自定义属性。 (那些不熟悉属性的人可能希望在继续之前阅读它们。)

This attribute, if present, provides the name of a class—an IDesigner—the designer can use to interface with the component. In effect, this class controls certain aspects of the designer and of the design-time behavior of the component. There's indeed quite a lot you can do with an IDesigner, but right now we're only interested in one thing.

此属性(如果存在)提供类的名称 - 一个IDesigner - 设计人员可以使用该名称与组件进行交互。实际上,此类控制设计器的某些方面以及组件的设计时行为。你可以用IDesigner做很多事情,但是现在我们只对一件事情感兴趣。

Most controls that use a custom IDesigner use one that derives from ControlDesigner, which itself derives from ComponentDesigner. The ComponentDesigner class has a public virtual property called AssociatedComponents, which is meant to be overridden in derived classes to return a collection of references to all "child" components of this one.

大多数使用自定义IDesigner的控件都使用从ControlDesigner派生的控件,ControlDesigner本身派生自ComponentDesigner。 ComponentDesigner类具有一个名为AssociatedComponents的公共虚拟属性,该属性旨在在派生类中重写,以返回对此节点的所有“子”组件的引用集合。

To be more specific, the ToolStrip control (and by inheritance, the MenuStrip control) has a DesignerAttribute that references a class called ToolStripDesigner. It looks sort of like:

更具体地说,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){
    ...
}

The ToolStripDesigner class is not public. It's internal to System.Design.dll. But since it's specified here by it's fully qualified name, the VS designer can use Activator.CreateInstance to create an instance of it anyway.

ToolStripDesigner类不公开。它是System.Design.dll的内部。但由于它是由完全限定名称指定的,因此VS设计者可以使用Activator.CreateInstance来创建它的实例。

This ToolStripDesigner class, because it inherits [indirectly] from ComponentDesigner has an AssociatedComponents property. When you call it you get a new ArrayList that contains references to all the items that have been added to the ToolStrip.

这个ToolStripDesigner类,因为它从ComponentDesigner继承[间接]具有AssociatedComponents属性。当您调用它时,您将获得一个新的ArrayList,其中包含对已添加到ToolStrip的所有项的引用。

So what would your code have to look like to do the same thing? Rather convoluted, but I think I have a working example:

那么你的代码看起来像做同样的事情是什么?相当令人费解,但我想我有一个有效的例子:

/*
 * 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);
}

#2


0  

The items such as ToolStripItem etc aren't actually controls, they are simply components that make up a ToolStrip or MenuStrip.

ToolStripItem等项目实际上不是控件,它们只是组成ToolStrip或MenuStrip的组件。

Which means, that if you want to include those components in your flattened list of controls then you will need to do the specific checks.

这意味着,如果要将这些组件包含在展平的控件列表中,则需要进行特定检查。

#3


0  

ToolStripControlHost might contain a Control:

ToolStripControlHost可能包含一个Control:

if (c is ToolStrip)
    foreach (ToolStripItem item in EnumerateTree(c, "Items"))
        if (item is ToolStripControlHost)
            AddControlsToList(
                new Control[] { ((ToolStripControlHost)item).Control },
                controlList);

...that's if you change argument 1 to type IEnumerable<Control> and write your own EnumerateTree function (I think it's great to have one good generic EnumerateTree method).

...如果您更改参数1以键入IEnumerable 并编写您自己的EnumerateTree函数(我认为拥有一个良好的通用EnumerateTree方法很棒)。