2010-04-22 22 views
16

Estoy diseñando un control de expansión simple.Control de usuario como contenedor en el momento del diseño

He derivado de UserControl, controles internos dibujados, construidos, ejecutados; todo bien.

Como un control interno es un panel, me gustaría utilizarlo como contenedor en el momento del diseño. De hecho, he utilizado los atributos:

[Designer(typeof(ExpanderControlDesigner))] 
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))] 

Great I say. Pero no es ...

El resultado es que puedo utilizarlo como contenedor en tiempo de diseño, sino:

  • Los controles agregados se remontan a los controles internos ya incrustados en el control de usuario
  • incluso si empujo al principio de un control añadido en tiempo de diseño, en tiempo de ejecución es de nuevo en controles incorporados al control de usuario
  • no puedo restringir el área de contenedores en tiempo de diseño en un área del panel

¿Qué me estoy perdiendo? Aquí está el código para completar ... ¿por qué este fragmento de código no funciona?

[Designer(typeof(ExpanderControlDesigner))] 
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))] 
public partial class ExpanderControl : UserControl 
{ 
    public ExpanderControl() 
    { 
     InitializeComponent(); 
.... 

[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] 
internal class ExpanderControlDesigner : ControlDesigner 
{ 
    private ExpanderControl MyControl; 

    public override void Initialize(IComponent component) 
    { 
     base.Initialize(component); 

     MyControl = (ExpanderControl)component; 

     // Hook up events 
     ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService)); 
     IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService)); 

     s.SelectionChanged += new EventHandler(OnSelectionChanged); 
     c.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving); 
    } 

    private void OnSelectionChanged(object sender, System.EventArgs e) 
    { 

    } 

    private void OnComponentRemoving(object sender, ComponentEventArgs e) 
    { 

    } 

    protected override void Dispose(bool disposing) 
    { 
     ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService)); 
     IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService)); 

     // Unhook events 
     s.SelectionChanged -= new EventHandler(OnSelectionChanged); 
     c.ComponentRemoving -= new ComponentEventHandler(OnComponentRemoving); 

     base.Dispose(disposing); 
    } 

    public override System.ComponentModel.Design.DesignerVerbCollection Verbs 
    { 
     get 
     { 
      DesignerVerbCollection v = new DesignerVerbCollection(); 

      v.Add(new DesignerVerb("&asd", new EventHandler(null))); 

      return v; 
     } 
    } 
} 

que he encontrado muchos recursos (Interaction, designed, limited area), pero nada era útil para ser operativo ...

En realidad no es un truco, ya que las clases pueden System.Windows.Forms estar diseñado (como es habitual) y tener un comportamiento correcto en tiempo de ejecución (TabControl, por ejemplo).

Respuesta

16

ParentControlDesigner no sabe lo que quiere hacer. Solo sabe que quiere que su UserControl sea un contenedor.

Lo que hay que hacer es poner en práctica su propio diseñador que permite el modo de diseño en el panel:

using System.ComponentModel; 
    using System.Windows.Forms; 
    using System.Windows.Forms.Design; 

    namespace MyCtrlLib 
    { 
     // specify my custom designer 
     [Designer(typeof(MyCtrlLib.UserControlDesigner))] 
     public partial class UserControl1 : UserControl 
     { 
      public UserControl1() 
      { 
       InitializeComponent(); 
      } 

      // define a property called "DropZone" 
      [Category("Appearance")] 
      [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
      public Panel DropZone 
      { 
       get { return panel1; } 
      } 
     } 

     // my designer 
     public class UserControlDesigner : ParentControlDesigner 
     { 
      public override void Initialize(System.ComponentModel.IComponent component) 
      { 
       base.Initialize(component); 

       if (this.Control is UserControl1) 
       { 
        this.EnableDesignMode(
         (UserControl1)this.Control).DropZone, "DropZone"); 
       } 
      } 
     } 
    } 

Esto lo aprendí de Henry Minute en CodeProject. Vea el enlace para algunas mejoras en la técnica.

+0

Funciona ! Pero en realidad no tiene los siguientes defectos: puede mover/dimensionar el "Área de trabajo" (¿qué diferencias tiene la "Zona de Descenso"?) pero en tiempo de ejecución no se refleja; los controles dopados en el momento del diseño no se pueden alinear en los límites del contenedor. – Luca

+0

Estoy teniendo esos mismos 2 problemas y me pregunto cómo se puede solucionar – Telanor

+0

No estoy seguro de cómo manejar esos problemas. ¿Miraste el enlace? Él podría haber resuelto aquellos con su versión mejorada. http://www.codeproject.com/KB/miscctrl/NestedControlDesigner.aspx –

2

Además de la respuesta anterior. En los comentarios se menciona que el usuario puede arrastrar WorkingArea. Mi solución para eso es incluir el panel WorkingArea en otro panel, configurándolo en Dock.Fill. Para no permitir que el usuario cambie de nuevo, he creado una clase ContentPanel que anula y oculta la propiedad Dock:

class ContentPanel : Panel 
{ 
    [Browsable(false)] 
    public override DockStyle Dock 
    { 
     get { return base.Dock; } 
     set { base.Dock = DockStyle.Fill; } 
    } 
} 

Para mí, esto hace que sea suficientemente seguro. Solo estamos usando el control internamente, por lo que principalmente queremos evitar que los desarrolladores accidentalmente arrastran las cosas. Sin duda, hay formas de arruinarlo de todos modos.

3

Para evitar que la zona de trabajo se mueva/cambiar de tamaño en el diseñador tiene que crear una clase para esa área de trabajo que oculta la ubicación, altura, anchura, las propiedades de tamaño del diseñador:

public class WorkingArea : Panel 
{ 
    [Browsable(false)] 
    [EditorBrowsable(EditorBrowsableState.Never)] 
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
    public new Point Location 
    { 
     get 
     { 
      return base.Location; 
     } 
     set 
     { 
      base.Location = value; 
     } 
    } 
... 
} 
Cuestiones relacionadas