Enable designer of child Panel in a UserControl

Sometimes you want to create a composite user control and allow users to interact with the user control at designer when they drop an instance of your user control on a form. For example, let’s say we are going to create a UserControl containing a title panel and a contents panel with following requirements:

  • Users should be able to drop controls on the contents panel at design time.
  • Users should not be able to drop controls on title panel.
  • Users should not be able to select title panel or change its properties.
  • Users should be able to select contents panel.
  • Users should be able to change properties of contents panel.
  • Users should not be able to move or delete contents panel.

In this post I’ll show how you can create a composite control (UserControl) and let the user to interact with a child control of your user control:

Prevent from drop item of forms:

Prevent from drop item of forms

Prevent from drop items from toolbox:

Prevent from drop items from toolbox

To satisfy above requirement, you need to create custom designers for the user control and for the contents panel.

Create the UserControl Create a user control having the following layout:

ContainerUserControl

Here is the code for the user control:

[Designer(typeof(MyUserControlDesigner))]
public partial class MyUserControl : UserControl
{
    public MyUserControl()
    {
        InitializeComponent();
        TypeDescriptor.AddAttributes(this.contentsPanel,
            new DesignerAttribute(typeof(MyPanelDesigner)));
    }
    public string Title
    {
        get { return titleLabel.Text; }
        set { titleLabel.Text = value; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Panel ContentsPanel
    {
        get { return contentsPanel; }
    }
}

In above code:

  • We have registered the designer of the user control
  • Also we registered the designer of the contents panel in constructor pf our user control. Since contents panel is a panel, it will use PanelDesigner automatically, but here by registering a new DesignerAttribute pointing to our custom panel designer, we will tell the designer to use our custom panel designer. The reason that we put it into constructor is because we want to be able to use PanelDesigner in design time of our user control, but we want to use our custom designer when we drop an instance of user control on the form.
  • We expose the contents panel as a readonly property of the control and will tell the designer to serialize its contents. Later in the custom designer for our user control, we will use this property to enable the designer for the content panel:

Create designer for user control

We need to create a custom designer for the user control. Here are the main responsibilities for the designer for our user control:

  • Enable designer for contents panel
  • Prevent users from dropping contents into user control. (The only part of user control which accepts dropping controls, is contents panel)

Here is the code for user control designer:

public class MyUserControlDesigner : ParentControlDesigner
{
    public override void Initialize(IComponent component)
    {
        base.Initialize(component);
        var contentsPanel = ((MyUserControl)this.Control).ContentsPanel;
        this.EnableDesignMode(contentsPanel, "ContentsPanel");
    }
    public override bool CanParent(Control control)
    {
        return false;
    }
    protected override void OnDragOver(DragEventArgs de)
    {
        de.Effect = DragDropEffects.None;
    }
    protected override IComponent[] CreateToolCore(ToolboxItem tool, 
        int x, int y, int width, int height, bool hasLocation, bool hasSize)
    {
        return null;
    }
}

In above code:

  • By calling EnableDesignMode method, we have enabled the contents panel at design time to allow user interaction with the contents panel.
  • We override CanParent method to prevent user from dragging control of the form into the user control.
  • We override CreateToolCore to prevent users from dropping controls from toolbox on the user control.
  • We override OnDragOver to show suitable mouse icon that shows dropping is not allowed on user control.

Create designer for contents panel

We need to create a custom designer for the contents panel. Here are the main responsibilities for the designer of our contents panel:

  • Prevent contents panel from moving, sizing and deleting.
  • Remove some properties like Dock, Visible, Location, Size, etc.
  • Remove Dock in Parent Container and Undock in Parent Container.

Here is the code for contents panel designer:

public class MyPanelDesigner : ParentControlDesigner
{
    public override SelectionRules SelectionRules
    {
        get
        {
            SelectionRules selectionRules = base.SelectionRules;
            selectionRules &= ~SelectionRules.AllSizeable;
            return selectionRules;
        }
    }
    protected override void PostFilterAttributes(IDictionary attributes)
    {
        base.PostFilterAttributes(attributes);
        attributes[typeof(DockingAttribute)] = new DockingAttribute(DockingBehavior.Never);
    }
    protected override void PostFilterProperties(IDictionary properties)
    {
        base.PostFilterProperties(properties);
        var propertiesToRemove = new string[] {
            "Dock", "Anchor",
            "Size", "Location", "Width", "Height",
            "MinimumSize", "MaximumSize",
            "AutoSize", "AutoSizeMode",
            "Visible", "Enabled",
        };
        foreach (var item in propertiesToRemove)
        {
            if (properties.Contains(item))
                properties[item] = TypeDescriptor.CreateProperty(this.Component.GetType(),
                    (PropertyDescriptor)properties[item],
                    new BrowsableAttribute(false));
        }
    }
}

I above code:

Download

You can clone or download the working example:

You May Also Like

About the Author: Reza Aghaei

I’ve been a .NET developer since 2004. During these years, as a developer, technical lead and architect, I’ve helped organizations and development teams in design and development of different kind of applications including LOB applications, Web and Windows application frameworks and RAD tools. As a teacher and mentor, I’ve trained tens of developers in C#, ASP.NET MVC and Windows Forms. As an interviewer I’ve helped organizations to assess and hire tens of qualified developers. I really enjoy learning new things, problem solving, knowledge sharing and helping other developers. I'm usually active in .NET related tags in stackoverflow to answer community questions. I also share technical blog posts in my blog as well as sharing sample codes in GitHub.

1 Comment

  1. This was amazingly helpful! I would have not figured out how to do this by myself, so thanks a lot!
    The only small hangup I had was understanding which namespaces I needed to import into my classes (System.Windows.Forms.Design, and System.ComponentModel ) and the additional assembly I needed to reference in my project (System.Drawing). But other that that, I appreciate your clear explanations about how this is all working. So, thanks again!

Leave a Reply

Your email address will not be published. Required fields are marked *