2008-11-17 8 views
2

J'ai du code qui générera génétiquement tous les contrôles dans un formulaire et les placera dans une liste. Voilà une partie du code:Énumération générique des éléments du contrôle .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); 
       // 
      } 
     } 

Je suis seulement capable d'utiliser c.HasChildren pour vérifier et voir s'il y a des contrôles enfants plus de ce contrôle racine.

Qu'en est-il d'un menuStrip, toolStrip et statusStrip? Comment puis-je obtenir tous les contrôles de ces contrôles de manière générique? Ex: MenuStripItem

Je sais que je pourrais essayer de tester le c.GetType() == typeof (MenuStrip) mais j'espérais ne pas avoir à faire de tests de type spécifiques.

Si j'ai besoin de donner plus d'informations, s'il vous plaît demander.

Merci un groupe

Répondre

6

je crois que le concepteur VS qu'il fait en obtenant une instance du concepteur du contrôle (voir le Designer attribute), et, si le concepteur est un ComponentDesigner, obtenir la propriété AssociatedComponents.

EDIT:

Bon, je suppose que c'est un peu vague. Un avertissement, cependant: ce qui suit est un peu compliqué, et pourrait ne pas valoir l'effort.

Une note sur la nomenclature:
Ci-dessous, je me référerai à la fois le concepteur de Visual Studio, qui est le nom utilisé pour faire référence à la fonctionnalité de Visual Studio par lequel la mise en page et le contenu des formulaires et des contrôles sont modifiés visuellement - et aux classes de concepteur - qui seront expliquées ci-dessous. Pour éviter toute confusion à laquelle je fais référence à un moment donné, je ferai toujours référence à la fonctionnalité de concepteur dans Visual Studio en tant que "concepteur", et je ferai toujours référence à une classe de concepteur comme un "IDesigner", qui est le interface chacun doit mettre en œuvre. Lorsque le concepteur Visual Studio charge un composant (généralement un contrôle, mais également Timer et autres), il recherche un attribut personnalisé sur la classe de type DesignerAttribute. (Ceux qui ne sont pas familiers avec les attributs peuvent vouloir read up on them avant de continuer.)

Cet attribut, s'il est présent, fournit le nom d'une classe, un IDesigner, que le concepteur peut utiliser pour s'interfacer avec le composant. En effet, cette classe contrôle certains aspects du concepteur et du comportement au moment du design du composant. Il y a en effet beaucoup de choses que vous pouvez faire avec un IDesigner, mais en ce moment nous ne sommes intéressés que par une chose.

La plupart des contrôles qui utilisent un IDesigner personnalisé en utilisent un qui dérive de ControlDesigner, lui-même dépendant de ComponentDesigner. La classe ComponentDesigner possède une propriété virtuelle publique appelée AssociatedComponents, qui est censée être remplacée dans les classes dérivées pour renvoyer une collection de références à tous les composants «enfants» de celle-ci.Pour être plus précis, le contrôle ToolStrip (et par héritage, le contrôle MenuStrip) a un DesignerAttribute qui fait référence à une classe appelée ToolStripDesigner. Il semble un peu comme:

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

La classe ToolStripDesigner n'est pas publique. C'est interne à System.Design.dll. Mais comme il est spécifié ici par son nom complet, le concepteur VS peut utiliser Activator.CreateInstance pour en créer une instance de toute façon.

Cette classe ToolStripDesigner, car elle hérite [indirectement] de ComponentDesigner a une propriété AssociatedComponents. Lorsque vous l'appelez, vous obtenez un nouveau ArrayList qui contient des références à tous les éléments qui ont été ajoutés au ToolStrip.

Alors qu'est-ce que votre code devrait ressembler à faire la même chose? Plutôt alambiquée, mais je pense avoir un exemple de travail:

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

Pouvez-vous expliquer un peu plus comment faire cela? Je n'ai jamais utilisé ces attributs auparavant ... – Miles

+0

Bon, j'ai essayé de l'expliquer complètement et de le démontrer (voir le post édité). Désolé, cela a pris tellement de temps. La vraie vie a un peu dérangé. Je déteste ça. –

0

Les éléments tels que ToolStripItem etc ne sont pas contrôle réellement, ils sont tout simplement des composants qui composent un ToolStrip ou MenuStrip.

Cela signifie que si vous souhaitez inclure ces composants dans votre liste de contrôles aplatie, vous devrez effectuer les vérifications spécifiques.

0

ToolStripControlHost peut contenir un contrôle:

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

... qui est si vous changez l'argument 1 de taper IEnumerable<Control> et écrire votre propre fonction de EnumerateTree (Je pense que c'est grand pour avoir une bonne méthode générique EnumerateTree).

Questions connexes