我有一个 C# WinForms“ListboxPanelControl”用户控件,它由一个 SplitContainer 组成,它在左侧的“Panel1”中托管一个用户绘制的列表框控件。 SplitContainer 的右侧“Panel2”将托管在项目添加到列表框中时添加的面板。因此,如果设计时用户将第一项添加到列表框,则关联的面板将添加到 SplitContainer 的“Panel2”。如果将第二个项目添加到列表框,则第二个面板将添加到 SplitContainer 的“Panel2”。然后,当用户选择列表框项目时,关联的面板会显示在右侧。
问题出现在设计时,当用户将控件(例如按钮)从工具箱拖放到当前事件的右侧面板(对应于当前选定的列表框项的面板)时。我以编程方式将它添加到适当的 Panel 的控件集合中,但这会导致 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 的目标托管控件已有效修复并预先知道。 使用我的控件,可以在设计时将其他控件放到它上面的目标托管面板是预先不知道的,因为我的 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”事件中,我为当前事件的面板调用“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 本身是动态的并且是动态构建的?
感谢您的帮助/见解! 托尼。
最佳答案
只是想我会提供一个更新,以防它对任何 future 的读者有用。我设法解决了“'child' 不是该父项的子控件”错误。问题是我在“当前事件”面板上调用“EnableDesignMode”,以允许将控件放到上面,但没有意识到“EnableDesignMode”方法的要求之一是:” child 指定的子控件是此控件设计者控件的子控件。” (http://msdn.microsoft.com/en-us/library/system.windows.forms.design.controldesigner.enabledesignmode.aspx)。 并且由于当前事件的面板是拆分容器右侧面板的子项,而不是“这个”整个父 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();
}
希望对某人有用...
关于c# - 在设计时向我的 'on-the-fly' 用户控件添加控件..?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12564323/