我正在编写一个Base UserControl,它将由一堆其他UserControl继承。我需要为所有这些后代控件强制执行某种设计(例如,一些按钮必须在顶部以及一个或两个标签)。
后代UserControl区域的其余部分可以随意拥有任何内容。
最初,我认为我可以将Panel放到Base UserControl上,设置Dock = Fill,后代控件的设计者将被强制将所有UI添加到这个所述面板中。然后,我可以将面板调整为我的内容。
但事实并非如此 - 当您将控件(例如GridView)拖放到后代UserControl上时,它会将其添加到后代用户控件的.Controls集合中,而不是我添加的Panel。
有没有办法从Base用户控件强制某个布局?
答案 0 :(得分:4)
简短的回答是“是”......然而,为了让这种行为深入研究编写自己的设计师的丑陋世界,您必须将每个控件与需要继承特殊位置的每个控件相关联。
以下链接应该是一个很好的起点,因为它概述了需要完成的工作以及提供示例代码。
http://support.microsoft.com/?id=813808
但是,请注意,这不是一项微不足道的任务。并且有很多代码(30多个文件)需要筛选才能理解如何实现设计器。我试过这个已经有两年了。
另一方面,您是否考虑过将设计更改为具有标签和按钮的单个父控件,并使用适当的子控件填充内容区域?也许甚至让你的子控件用于内容区域实现一个特定的接口来保证合同,以便父母可以与他们交互而不必知道特定的类名?
答案 1 :(得分:4)
AngryHacker -
我认为这很容易,并且发现自己踏上了进入WinForm设计时期土地的有趣旅程。这绝对不像在VB6中那么容易:-)。
无论如何,经过一些研究,我发现了一些名为 EnableDesignMode()的方法的引用。但是,无法从WinForm组件直接访问它。这必须从子类 ParentControlDesigner 的类中调用,该类通过 Designer 属性注入到UserControl中。在这个项目中,我调用了子类 ButtonBarDesigner ,并重写了 Initialize()方法,这允许我告诉设计者组件 ButtonBar 有一个子组件 fillPanel ,可以通过公共属性“FillPanel”访问。
此时,它似乎正在发挥作用。我设法将一个控件放到ButtonBar上,它出现在填充面板中。但是,如果您保存了表单,然后重新加载它,则结果是控件已实例化,但未放置在ButtonBar控件中。似乎有一个偷偷摸摸的最后一点, EnableDesignMode()的文档很方便地遗漏了。您需要在 FillPanel 属性上具有 DesignerSerializationVisibility 属性。添加此属性可以实现此功能。
您需要在项目中引用 System.Design 才能使用设计时间。
以下代码适用于推定的基类ButtonBar:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Threading;
namespace ForceUserControl
{
[Designer(typeof(ButtonBarDesigner))]
public partial class ButtonBar : UserControl
{
public ButtonBar()
{
InitializeComponent();
}
/// <summary>
/// Returns inner panel.
/// </summary>
/// <remarks>Should allow persistence.</remarks>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel FillPanel
{
get { return fillPanel; }
}
}
private class ButtonBarDesigner : ParentControlDesigner
{
public override void Initialize(IComponent component)
{
base.Initialize(component);
Panel fillPanel = ((ButtonBar)component).FillPanel;
// The name should be the same as the public property used to return the inner panel control.
base.EnableDesignMode(fillPanel, "FillPanel");
}
}
}