4

我有一个 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/文章/37830/设计嵌套控件

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

我尝试过以更灵活的方式使用“DesignerSerializationVisibility”和“EnableDesignMode”方法,但似乎仍然遇到上述“子不是此父级的子控件”错误。

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

这是在 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”事件中,我为当前活动的面板调用“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 本身是动态的并且是动态构建的?

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

4

1 回答 1

2

只是想我会提供更新,以防它对任何未来的读者有用。我设法解决了“'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();
}

希望这对某人有用...

于 2012-10-04T14:21:17.003 回答