【问题标题】:Adding controls to my 'on-the-fly' user-control at design-time..?在设计时向我的“动态”用户控件添加控件..?
【发布时间】:2012-09-24 11:39:23
【问题描述】:

我有一个 C# WinForms 'ListboxPanelControl' UserControl,它由一个 SplitContainer 组成,它在左侧的“Panel1”中托管一个用户绘制的 Listbox 控件。 SplitContainer 的右侧“Panel2”将承载在项目添加到列表框中时添加的面板。因此,如果设计时用户将第一项添加到 Listbox,则关联的 Panel 将添加到 SplitContainer 的“Panel2”。如果将第二个项目添加到列表框,则会将第二个面板添加到 SplitContainer 的“Panel2”。然后,当用户选择 Listbox 项目时,关联的 Panel 会显示在右侧。

问题出现在设计时,当用户将控件(例如按钮)从工具箱拖放到当前活动的右侧面板(与当前选定的列表框项对应的面板)上时。我以编程方式将它添加到适当的 Panel 的 Controls 集合中,但这会导致 Microsoft Visual Studio IDE 中出现 "'child' is not a child control of this parent" 错误

我研究了这个问题并发现了一些有用的文章,展示了“如何允许作为另一个控件的子控件的控件在设计时接受将控件放到它上面”,例如:http://www.codeproject.com/Articles/37830/Designing-Nested-Controls

这将“DesignerSerializationVisibility”属性与 ControlDesigner 的“EnableDesignMode”方法结合使用,以允许 VS IDE 处理将控件放置到另一个控件上。问题在于,在这种情况下,UserControl 的目标宿主 Control 是有效固定的并且预先知道。 使用我的控件,可以在设计时将其他控件拖放到其上的目标托管面板是未知的,因为我的 UserControl 本身可以“即时”构建(当用户添加更多列表框项目导致更多潜在的托管面板)。

我尝试过以更灵活的方式使用“DesignerSerializationVisibility”和“EnableDesignMode”方法,但似乎仍然遇到上述“child is not a child control of this parent”的错误。

我希望这一切都有意义!?代码目前看起来像这样:

这是在 ListboxPanelControl 类中: ...

    [
    Category("Appearance"),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
    ]
    public Panel MainPanel
    {
        get
        {
            //Instead of returning a 'fixed' Panel we return whatever the currently
            //active Panel is according to the currently selected Listbox item
            if( mnCurrentlyActiveListBoxIndex >= 0 )
            {
                ListBoxEntry lEntry = (ListBoxEntry)listBox.Items[mnCurrentlyActiveListBoxIndex];
                return lEntry.RelatedPanel;
            }

            return null;
        }
    }

然后,在 ListBoxPanelControl 的“OnControlAdded”事件中,我为当前活动的 Panel 调用“EnableDesignMode”(通过简单的“SetDesignMode”包装器),然后添加控件:

protected override void OnControlAdded(ControlEventArgs e)
{
...
mDesigner.SetDesignMode(MainPanel, "MainPanel" + mnCurrentlyActiveListBoxIndex.ToString());
                    ListBoxEntry lEntry = (ListBoxEntry)listBox.Items[mnCurrentlyActiveListBoxIndex];
                    lEntry.RelatedPanel.Controls.Add(e.Control);
                    e.Control.BringToFront();
                    e.Control.Refresh();
...
}

我希望我已经足够清楚地解释了这个问题!基本上,有没有人设法将一个控件添加到他们的 WinForms UserControl,其中目标托管控件本身就是一个子控件,并且当他们的 UserControl 本身是动态的并且是动态构建的?

感谢您的帮助/见解! 托尼。

【问题讨论】:

    标签: c# .net winforms dynamic-usercontrols


    【解决方案1】:

    只是想我会提供一个更新,以防它对任何未来的读者有用。我设法解决了“'child'不是这个父级的子控件”错误。问题是我在“当前活动”面板上调用“EnableDesignMode”,以允许将控件拖放到它上面,但没有意识到“EnableDesignMode”方法的要求之一是:” child 指定的子控件是此控件设计器控件的子控件。" (http://msdn.microsoft.com/en-us/library/system.windows.forms.design.controldesigner.enabledesignmode.aspx)。 并且因为当前活动面板是拆分容器右侧面板的子项,而不是“这个”整体父 ListboxPanel 控件,所以它会抱怨!

    最后,我意识到我们还需要对控件进行序列化,经过多次痛苦和反复试验,我们最终还是没有使用“EnableDesignMode”!相反,要走的路似乎是使用设计器的服务:IComponentChangeService、IDesignerHost 和 ISelectionService。我不只是以传统方式添加控件(如“Controls.Add()”),而是使用 IDesignerHost 的“CreateComponent”方法,以便设计人员“正式”了解它。我们将每个操作包装在一个事务中,以便它可以“撤消/重做”。有点像这样:

    DesignerTransaction designerTransaction = m_designHost.CreateTransaction("Add Page");
    try
    {
        Panel p = (Panel)m_designHost.CreateComponent(typeof(Panel));
    
        m_changeService.OnComponentChanging(mControl.MainPanel, TypeDescriptor.GetProperties(mControl.MainPanel)["Controls"]);
    
        ...
        ...
        //Add our Panel to our splitContainer Panel2's controls collection, etc
    
        ...
        m_changeService.OnComponentChanged(mControl.MainPanel, TypeDescriptor.GetProperties(mControl.MainPanel)["Controls"], null, null);
    
        //Use the selection service to select the newly added Panel
        m_selectionService.SetSelectedComponents(new object[] { p });
    
    }
    catch( Exception ex )
    {
         designerTransaction.Cancel();
    }
    finally
    {
         designerTransaction.Commit();
    }
    

    希望对某人有用...

    【讨论】:

    • 谢谢。我自己也在做类似的事情,这可能会有所帮助。一个快速说明:无论是否有异常,您的 finally 块都会执行。取消后调用designerTransaction.Commit()可以吗?
    • 设计师主机在哪里发挥作用?你在哪里执行了这段代码?是在设计师课上吗?
    猜你喜欢
    • 1970-01-01
    • 2013-02-26
    • 2017-01-06
    • 1970-01-01
    • 2010-12-07
    • 2012-12-10
    • 2017-01-10
    相关资源
    最近更新 更多