C# 将控件添加到my';在飞行中';设计时的用户控制。。?

C# 将控件添加到my';在飞行中';设计时的用户控制。。?,c#,.net,winforms,dynamic-usercontrols,C#,.net,Winforms,Dynamic Usercontrols,我有一个C#WinForms“ListboxPanelControl”用户控件,它由一个SplitContainer组成,该容器在左侧的“Panel1”中承载一个用户绘制的Listbox控件。SplitContainer右侧的“Panel2”将承载在项目添加到列表框时添加的面板。因此,如果设计时用户将第一项添加到列表框中,则会将关联的面板添加到SplitContainer的“Panel2”中。如果第二个项目添加到列表框,则第二个面板将添加到SplitContainer的“Panel2”。然后,当

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

问题出现在设计时,当用户将控件(例如按钮)从工具箱拖放到当前活动的右侧面板(与当前选定的Listbox项对应的面板)上时。我以编程方式将其添加到相应面板的控件集合中,但这会导致Microsoft Visual Studio IDE中出现“'child'不是此父控件的子控件”错误

我研究了这个问题,发现了一些有用的文章,展示了“如何允许另一个控件的子控件在设计时接受将控件放到它上面”,例如:

这将使用“DesignerSerializationVisibility”属性与ControlDesigner的“EnableDesignMode”方法相结合,以允许VS IDE处理将控件拖放到另一个控件上的操作。问题是在这个场景中,UserControl的目标宿主控件是有效固定的,并且是预先知道的对于my control,在设计时可以将其他控件放置在其上的目标托管面板,由于my UserControl本身可以“动态”构建(随着用户添加更多列表框项目,从而产生更多潜在的托管面板),因此事先不知道它的目标托管面板。

我尝试以更灵活的方式使用“DesignerSerializationVisibility”和“EnableDesignMode”方法,但似乎仍然遇到前面提到的“child不是此父控件的子控件”错误

我希望这一切都有意义!?代码当前看起来如下所示:

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();
}
这在ListboxPanelControl类中:

然后,在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本身是动态的且正在动态构建时

感谢您的帮助/见解!
Tony。

我只是想提供一个更新,以防对未来的读者有用。我设法解决了“'child'不是此父控件的子控件”错误。问题是,我在“当前活动”面板上调用“EnableDesignMode”,以允许将控件放到面板上,但没有意识到“EnableDesignMode”方法的要求之一是:“child指定的子控件是此控件设计器控件的子控件。” (). 而且因为当前活动面板是拆分容器右侧面板的子面板,而不是“此”整体父ListboxPanel控件,所以它抱怨道

最后,我意识到我们还需要序列化控件,在经历了许多痛苦和尝试和错误之后,我们最终还是没有使用“EnableDesignMode”!相反,方法似乎是使用设计器的服务:IComponentChangeService、IDesignerHost和ISelectionService。我使用IDesignerHost的“CreateComponent”方法,而不是像“Controls.Add()”)那样以传统的方式添加控件,以便设计者“正式”了解它的所有内容。我们将每个操作包装在一个事务中,以便可以“撤消/重做”。有点像这样:

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()可以吗?设计器宿主在哪里发挥作用?你是在哪里执行这段代码的?是在设计课上吗?