views:

313

answers:

1

I'm designing a simple expander control.

I've derived from UserControl, drawn inner controls, built, run; all ok.

Since an inner Control is a Panel, I'd like to use it as container at design time. Indeed I've used the attributes:

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

Great I say. But it isn't...

The result is that I can use it as container at design time but:

  • The added controls go back the inner controls already embedded in the user control
  • Even if I push to top a control added at design time, at runtime it is back again on controls embedded to the user control
  • I cannot restrict the container area at design time into a Panel area

What am I missing? Here is the code for completeness... why this snippet of code is not working?

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

I've found many resources (Interaction, designed, limited area), but nothing was usefull for being operative...

Actually there is a trick, since System.Windows.Forms classes can be designed (as usual) and have a correct behavior at runtime (TabControl, for example).

+1  A: 

ParentControlDesigner doesn't know what you want do. It only knows you want your UserControl to be a container.

What you need to do is implement your own designer which enables design mode on the 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");
                }
            }
        }
    }

I learned this from Henry Minute on CodeProject. See the link for some improvements on the technique.

Charles
It works! But actually it isn't has the following defects: you can move/size the "WorkingArea" (which differences has the "DropZone"?) but at runtime it isn't reflected; the dopped controls at design time cannot be aligned on container bounds.
Luca
I'm having those same 2 problems and wondering how that can be fixed
Telanor
Off the top of my head I'm not sure how to handle those issues. Did you look at the link? He might have solved those with his enhanced version. http://www.codeproject.com/KB/miscctrl/NestedControlDesigner.aspx
Charles
Yes. I've red carefully the article, but the behavior it's the same. Apart from designer integration, it seems that the settings of the control are not stored in the source, since at runtime is has all default values.
Luca