枚举.Net控件的项目一般(MenuStrip,ToolStrip,StatusStrip) [英] Enumerate .Net control's items generically (MenuStrip, ToolStrip, StatusStrip)

查看:81
本文介绍了枚举.Net控件的项目一般(MenuStrip,ToolStrip,StatusStrip)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码可以将所有控件以一种形式获取并将它们放在列表中。以下是一些代码:

 私人列表< Control> GetControlList(Form parentForm)
{
List< Control> controlList =新的List< Control>();
AddControlsToList(parentForm.Controls,controlList);

返回controlList;
}

private void AddControlsToList(Control.ControlCollection rootControls,List< Control> controlList)
{
foreach(rootControls中的Control c)
{
controlList.Add(c);
if(c.HasChildren)
AddControlsToList(c.Controls,controlList);
//
}
}

所以我只是能够使用c.HasChildren检查并查看此根控件中是否还有其他子控件。



menuStrip,toolStrip和statusStrip呢?我如何获得这些控件中的所有控件的一般信息?例如:MenuStripItem



我知道我可以尝试测试c.GetType()== typeof(MenuStrip),但我希望不必这样做



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



谢谢!

解决方案

我相信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的类。看起来像这样:

  / * 
*注意,在C#中,我可以引用 DesignerAttribute在[方括号]
*中的类,只需使用 Designer即可。编译器会在我们的末尾添加属性(假设
*没有名为 Designer的属性类)。
* /
[Designer( System.Windows.Forms.Design.ToolStripDesigner,System.Design,Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b03f5f7f11d50a3a),...(其他属性)]
公共类ToolStrip:ScrollableControl,IArrangedElement,...(其他接口){
...
}

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



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



那么您要做的 代码看起来应该像什么一样?相当复杂,但我想我有一个可行的示例:

  / * 
*有些控件要求我们设置
之前的网站属性*我们将IDesigner与它们关联。
* IDesigner使用此站点从设计器获取服务。因为我们不是
*实现真正的设计师,所以我们将创建一个虚拟站点,该站点
*提供最低限度的服务,并且依赖于框架
*来提供尽可能多的功能可能。
* /
类DummySite:ISite,IDisposable {
DesignSurface designSurface;
IComponent组件;
字符串名称;

公共IComponent组件{get {return component;}}
公共IContainer容器{get {return designSurface.ComponentContainer;}}
public bool DesignMode {get {return false;} }
public string Name {get {返回名称;} set {name = value;}}

public DummySite(IComponent component){
this.component = component;
designSurface = new DesignSurface();
}
〜DummySite(){Dispose(false);}

受保护的虚拟无效值Dispose(bool isDispose){
if(isDispose)
designSurface .Dispose();
}

public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}

公共对象GetService(Type serviceType){return designSurface.GetService(serviceType);}
}

静态void GetComponents(IComponent组件, int level,Action< IComponent,int> action){
action(component,level);

bool可见,已启用;
Control control =组件为Control;
if(control!= null){
/ *
*附加IDesigner会将Visible和Enabled属性设置为true。
*在Visual Studio中设计表单时很有用,但是在
*运行时,我们宁愿控件保持其状态,因此我们将保存
*值这些属性并在分离IDesigner之后将其还原。
* /
visible = control.Visible;
enabled = control.Enabled;

foreach(控制子项为Control.Controls)
GetComponents(子项,级别+ 1,操作);
}其他可见=启用=假;

/ *
* TypeDescriptor类有一个方便的静态方法,可获取
*组件类型的DesignerAttribute,我们将其传递给
*并创建一个实例对我们来说是IDesigner类。
*为我们省了很多麻烦。
* /
ComponentDesigner des = TypeDescriptor.CreateDesigner(component,typeof(IDesigner))作为ComponentDesigner;
if(des!= null)
try {
DummySite网站;
if(component.Site == null)
component.Site = site = new DummySite(component);
else site = null;

try {
des.Initialize(component);
foreach(des.AssociatedComponents中的IComponent子级)
GetComponents(child,level + 1,action);
}最终{
if(site!= null){
component.Site = null;
site.Dispose();
}
}
}最终{des.Dispose();}

if(control!= null){
control.Visible = visible;
control.Enabled =已启用;
}
}


/ *我们将在ListComponents调用中使用它* /
[DllImport( user32.dll,CharSet = CharSet.Auto)]
静态外部int SendMessage(IntPtr hWnd,int msg,int wParam,int lParam);

const int WM_SETREDRAW = 11;

void ListComponents(){
/ *
*不可见的控件和禁用的控件将在GetComponents调用期间临时显示并启用
*(请参阅其中的注释)调用),因此为了防止
*它们出现然后再次消失(或暂时出现
*更改启用状态),我们将禁用重新绘制窗口并重新启用它b *之后。
* /
SendMessage(句柄,WM_SETREDRAW,0,0);
GetComponents(this,0,
/ *您将在这里做一些更有用的事情* /
(component,level)=> System.Diagnostics.Debug.WriteLine(new string ( \t,级别)+组件));
SendMessage(Handle,WM_SETREDRAW,1,0);
}


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.

What about a menuStrip, toolStrip, and statusStrip? How do I get all of the controls that are in these controls generically? Ex: 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.

If I need to give more info, please ask.

Thanks a bunch

解决方案

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.

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.

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

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.

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.

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:

/*
 * 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.

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.

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

这篇关于枚举.Net控件的项目一般(MenuStrip,ToolStrip,StatusStrip)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆