0
votes

I have a specific requirement to create a user control with specific common functions. To that control I also have the requirement to allow other developers to add controls in designer mode to make specific UI's. To do this I created a user control, adding (sample) label, and button. I also added a panel to allow adding of addition controls in a specific area of the control.

I then made the made the class visible in designer mode by adding [Designer] markup and a [ControlDesigner]. This gives the desired effect to add a User control with some fixed content, and add more controls to the page. The problem is that the panel can be moved by the user in design mode, and VisualStudio gets confused, creating a circular reference.. I must be missing something? Can I turn off the resizing/positioning of the panel, even though I need design mode enabled?

NOTE: I also tried to just use a user control in design mode, but added controls keep disappearing behind the fixed controls on the User Control.

Code and examples are below.. Any suggestion/fixes welcomed..

Above is the visual of the user control with the panel

enter image description here

Above is a form including the User control, and adding a custom button to the panel.. Note the panel drag is enable, if touched, a circular reference gets created in the form.designer.cs file, and the project becomes unstable.

Finally below is the class for User Control

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

using System.Windows.Forms.Design;

namespace wfcLib
{

    [DesignerAttribute(typeof(MyControlDesigner))]
    [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
    public partial class ucInput : UserControl
    {

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Panel InternalPanel
        {
            get { return pnlContent; }
            set { pnlContent = value; }
        }
        public ucInput()
        {
            InitializeComponent();

        }

    }
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    public class MyControlDesigner : System.Windows.Forms.Design.ControlDesigner
    {
        public override void Initialize(IComponent c)
        {
            base.Initialize(c);
            ucInput ctl = (ucInput)c;
            EnableDesignMode(ctl.InternalPanel, "InternalPanel");
        }
    }
}
1
You should use a derived Panel with its own designer. Then in the panel designer override the SelectionRules property to prevent resizing and moving.TnTinMn

1 Answers

0
votes

In addition to my comment concerning using a derived Panel with its own designer that overrides the SelectionRules property, another method would be to tap into the designer's ISelectionService to detect a change in selected components and remove the panel if it was selected.

This is accomplished by overriding the control's Site property to set the hook. Also note that I changed the InternalPanel property to be read-only as you really do not want that writable.

[DesignerAttribute(typeof(MyControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ucInput : UserControl
{

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Panel InternalPanel
    {
        get { return pnlContent; }
    }

    public ucInput()
    {
        InitializeComponent();
    }

    private ISelectionService selectionService;
    private IDesignerHost host;
    public override ISite Site
    {
        get
        {
            return base.Site;
        }
        set
        {
            host = null;
            UnSubscribeFromSelectionService();
            base.Site = value;
            if (value != null)
            {
                host = (IDesignerHost)this.Site.GetService(typeof(IDesignerHost));
                if (host != null)
                {
                    if (host.Loading)
                    {
                        // defer subscription to selection service until fully loaded
                        host.Activated += Host_Activated;
                    }
                    else
                    {
                        SubscribeToSelectionService();
                    }
                }
            }
        }
    }

    private void Host_Activated(object sender, EventArgs e)
    {
        host.Activated -= Host_Activated;
        SubscribeToSelectionService();
    }

    private void SubscribeToSelectionService()
    {
        selectionService = (ISelectionService)this.Site.GetService(typeof(ISelectionService));
        if (selectionService != null)
        {
            selectionService.SelectionChanging += OnSelectionChanging;
        }
    }

    private void UnSubscribeFromSelectionService()
    {
        if (selectionService != null)
        {
            selectionService.SelectionChanging -= OnSelectionChanging;
        }
    }

    private void OnSelectionChanging(object sender, EventArgs e)
    {
        if (selectionService.GetComponentSelected(pnlContent))
        {
            selectionService.SelectionChanging -= OnSelectionChanging;
            selectionService.SetSelectedComponents(new[] { pnlContent }, SelectionTypes.Remove);
            selectionService.SelectionChanging += OnSelectionChanging;
        }
    }
}

Edit: The original code neglected to account for SelectionService not being available while the IDesignerHost is loading. Added code to defer subscription until the IDesignerHost is activated.