嵌套面板:'孩子'不是这个父母的孩子控制

时间:2014-12-07 17:48:32

标签: c# panel

我有一个继承自Panel的类。它包含嵌套在其中的第二个面板。当控件添加到此控件时,我实际上希望将它们添加到内部面板中。这里的目标是使用外部面板绘制一个特殊边框,但随后可以在内部面板内部托管控件,就像它是任何其他面板一样。

以下是我正在使用的基本代码:

public class TestPanel2 : Panel
{
    private Panel innerPanel = new Panel();

    public TestPanel2()
    {
        this.Controls.Add(innerPanel);
        this.ControlAdded += new ControlEventHandler(TestPanel2_ControlAdded);
    }

    void TestPanel2_ControlAdded(object sender, ControlEventArgs e)
    {
        if (e.Control != innerPanel) {
            innerPanel.Controls.Add(e.Control);
        }
    }
}

在Designer中使用此控件时,将子控件(例如CheckBox)拖入其中会导致设计器报告:

'child' is not a child control of this parent

我的理论是Designer为了自己的目的而调用Controls.SetChildIndex()或Controls.GetChildIndex(),这就是触发错误。所以我尝试将以下属性添加到类中:

    public new ControlCollection Controls
    {
        get { return innerPanel.Controls; }
    }

当我这样做时,我也改变了this.Controls to base.Controls的所有内部引用。但是,这并没有解决问题。

有没有办法添加一个自动接收拖入控件的嵌套面板?如果我更改代码,以便子控件只在运行时添加到innerControl,它可以工作,但子控件的位置最终是错误的,所以它不是一个解决方案。

更新

无论价值多少,这里都是我想要做的简化图。我正在创建一个将由其他开发人员使用的工具包。它是一个包含自定义边框和标题栏的专用面板。可以认为它在功能上类似于“GroupBox”控件。我希望他们能够将这个专用面板拖到他们的表单上,然后在Designer中添加控件。 “innerPanel”需要是它自己的面板,因此它是滚动的唯一区域(当需要滚动时)。

title block panel diagram http://cosmicjive.net/tempimages/NestedPanel.png

3 个答案:

答案 0 :(得分:0)

您必须先删除控件,然后将其添加到内部面板。控件不能同时位于两个通道上:

void TestPanel2_ControlAdded(object sender, ControlEventArgs e)
{
    if (e.Control != innerPanel) {
        this.Controls.Remove(e.Control);
        innerPanel.Controls.Add(e.Control);
    }
}

答案 1 :(得分:0)

Hans Passant通过链接到这个讨论指出了我正确的方向:

How do I provide designer support to a TabControl residing in a UserControl, so that I can drag/drop controls onto tab pages?

我还找到了一个示例项目,它几乎展示了我想要创建的确切控件:

http://www.codeproject.com/Articles/37830/Designing-Nested-Controls

以下是我对该控件的修订版本:

[Designer(typeof(TestUserControlDesigner))]
public partial class TestPanel3 : UserControl
{
    private Panel innerPanel = new Panel();

    public TestPanel3()
    {
        InitializeComponent();
        this.Controls.Add(innerPanel);
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Panel ContentPanel
    {
        get { return innerPanel; }
    }
}

internal class TestUserControlDesigner : ParentControlDesigner
{
    public override void Initialize(System.ComponentModel.IComponent component)
    {
        base.Initialize(component);
        EnableDesignMode((this.Control as TestPanel3).ContentPanel, "ContentPanel");
    }
}

这种方法有效,尽管" innerPanel"可以"被拖出" Designer中的控件。但是对于这个问题还有其他解决方案,否则这是一个很好的解决方案。

答案 2 :(得分:0)

我知道这已有几个月了,但请参阅此博客文章,了解如何使内部控件成为用户控件的内容控件。它将使您的生活变得更加轻松,因为控件只能放在您的内部面板上。

How can I drop controls within UserControl at Design time? 需要考虑的几件事:   1.在设计用户控件时,您希望内部面板可移动且相当大,但是当您将UserControl放在另一个设计图面上时则不行。

  1. 为了实现这一点,我创建了一个ContentPanel,它继承了Panel,然后使用自定义设计器在设计时根据需要删除了大小调整夹点
  2. 一个。这是外部面板的设计者 - 我的实现将它嵌套在外部面板中,因此可以使用类似于Designer的属性来引用它,该属性应用于外部类

    [Designer(typeof(YourUserControl.Designer))]
    public partial class YourUserControl : UserControl
    
    #region Designer - Friend class
    
        /// <summary>
        /// Exposes the internal panel as content at design time, 
        /// allowing it to be used as a container for other controls.
        /// 
        /// Adapted
        /// From: How can I drop controls within UserControl at Design time?
        /// Link: http://blogs.msdn.com/b/subhagpo/archive/2005/03/21/399782.aspx
        /// </summary>
        internal class Designer : ParentControlDesigner
        {
            public override void Initialize(IComponent component)
            {
                base.Initialize(component);
    
                var parent = (YourUserControl)component;
                EnableDesignMode(parent.Content, "Content");
            }
        }
    
        #endregion
    

    B中。以下是需要添加到外部面板的Content属性

    #region Content - Used by the designer class
    
        /// <summary>
        /// Defines the control which can act as a container at design time. 
        /// In conjunction with other design time attributes and the designer
        /// defined below, allows the user control to act as a container at
        /// design time. This means other controls can be sited on the content
        /// panel, such as a text boxes or labels, etc.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public ContentPanel Content
        {
            get 
            {
                return this.contentPanel1;
            }
        }
    
        #endregion
    

    ℃。这是用作内容面板的内部面板。在您的情况下,您需要在下面的代码中切换Dock和Anchor属性。我希望它始终停靠,而您希望它始终锚定,因为您在帖子中引用了标题和边框。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Linq;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace j2associates.Tools.Winforms.Controls
    {
        [Designer(typeof(ContentPanel.Designer))]
        public class ContentPanel : Panel
        {
            private ScrollableControl _parentScrollableControl;
    
            public ContentPanel()
                : base()
            {
                // Dock is always fill.
                this.Dock = DockStyle.Fill;
            }
    
            protected override void OnParentChanged(EventArgs e)
            {
                base.OnParentChanged(e);
    
                if (this.Parent != null)
                {
                    Control parent = this.Parent;
                    while (parent != null && this.Parent.GetType() != typeof(ScrollableControl))
                    {
                        parent = parent.Parent;
                    }
                    if (parent != null && parent.GetType() == typeof(ScrollableControl))
                    {
                        _parentScrollableControl = (ScrollableControl)parent;
    
                        // Property value is retrieved from scrollable control panel.
                        this.AutoScroll = _parentScrollableControl.AutoScroll;
                    }
                }
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
    
                if (_parentScrollableControl != null)
                {
                    this.AutoScroll = _parentScrollableControl.AutoScroll;
                }
            }
    
            protected override void OnEnter(EventArgs e)
            {
                base.OnEnter(e);
    
                this.AutoScroll = _parentScrollableControl != null ? _parentScrollableControl.AutoScroll : false;
            }
    
            #region Designer - Friend class
    
            /// <summary>
            /// Allows us to handle special cases at design time.
            /// </summary>
            internal class Designer : ParentControlDesigner
            {
                private IDesignerHost _designerHost = null;
                private Control _parent = null;
    
                #region Overrides
    
                #region Initialize
    
                public override void Initialize(IComponent component)
                {
                    base.Initialize(component);
    
                    // Used to determine whether the content panel is sited on a form or on a user control.
                    _designerHost = (IDesignerHost)this.GetService(typeof(IDesignerHost));
                    _parent = ((ContentPanel)component).Parent;
                }
    
                #endregion
    
                #region SelectionRules
    
                public override SelectionRules SelectionRules
                {
                    get
                    {
                        SelectionRules selectionRules = base.SelectionRules;
    
                        // When hosted on a form, remove all resizing and moving grips at design time
                        // because the content panel is part of a composed user control and it cannot
                        // be moved nor can the dock property change.
                        //
                        // When not hosted on a form, then it is part of a user control which is being
                        // composed and it can be moved or the dock property changed.
                        if (!ReferenceEquals(_designerHost.RootComponent, _parent))
                        {
                            selectionRules = SelectionRules.Visible | SelectionRules.Locked;
                        }
    
                        return selectionRules;
                    }
                }
    
                #endregion
    
                #region PreFilterProperties
    
                protected override void PreFilterProperties(System.Collections.IDictionary properties)
                {
                    base.PreFilterProperties(properties);
    
                    // The Anchor property is not valid for a ContentPanel so just get rid of it.
                    properties.Remove("Anchor");
                }
    
                #endregion
    
                #region PostFilterProperties
    
                protected override void PostFilterProperties(System.Collections.IDictionary properties)
                {
                    // Hide the Anchor property so it cannot be changed by the developer at design time.
                    PropertyDescriptor dockDescriptor = (PropertyDescriptor)properties["Dock"];
                    dockDescriptor = TypeDescriptor.CreateProperty(dockDescriptor.ComponentType, dockDescriptor, new Attribute[] { new BrowsableAttribute(false), new EditorBrowsableAttribute(EditorBrowsableState.Never)} );
                    properties[dockDescriptor.Name] = dockDescriptor;
    
                    // Hide the AutoScroll property so it cannot be changed by the developer at design time
                    // because it is set from the nearest panel of type scrollable control.
                    PropertyDescriptor autoScrollDescriptor = (PropertyDescriptor)properties["AutoScroll"];
                    autoScrollDescriptor = TypeDescriptor.CreateProperty(autoScrollDescriptor.ComponentType, autoScrollDescriptor, new Attribute[] { new ReadOnlyAttribute(true) });
                    properties[autoScrollDescriptor.Name] = autoScrollDescriptor;
    
                    // Make the Name property read only so it cannot be changed by the developer at design time
                    // because it is set from the nearest panel of type scrollable control.
                    PropertyDescriptor nameDescriptor = (PropertyDescriptor)properties["Name"];
                    nameDescriptor = TypeDescriptor.CreateProperty(nameDescriptor.ComponentType, nameDescriptor, new Attribute[] { new ReadOnlyAttribute(true) });
                    properties[nameDescriptor.Name] = nameDescriptor;
    
                    // Always call the base method last.
                    base.PostFilterProperties(properties);
                }
    
                #endregion
    
                #endregion
            }
    
            #endregion
        }
    }
    

    ...享受