转自:点击打开链接
最近在设计一个带标题栏的自定义控件时候,遇到很奇怪的问题——不能向组合控件中添加组件。原以为不存在或是很简单的问题,竟然被拦住好久。这个问题导致的主要问题包括:
1. 不能向组合控件中添加组件。
2. 部分方法如重载OnControlAdded方法等,会导致添加到子容器中的组件设计时或运行时不可见等问题。
虽然最终发现问题很简单,不过在google中没有发现中文的资料(可能搜索水平太差),先将Henry Minute 写在CodeProject的文章标记如此,以作备忘。
谢谢BillW @stackoverflow.com
If you are dealing with the problem of how a "child" Control of a UserControl placed on a Form at Design-Time can be made to function as a container onto which you can drag-and-drop other controls from the Toolbox : this CodeProject article by Henry Minute may be helpful : Designing Nested Controls. For example : you have a UserControl with a Panel inside it : an instance of the UserControl is placed on a Form : in the Design-time view of the Form : you want to be able to drag-drop controls onto the Panel in the UserControl and have them become child controls of the Panel : Henry's article will show you how to do that.
This from Microsoft : How to make a UserControl object acts as a control container design-time by using Visual C#
解决方案(代码来自Henry Minute):
1. 为自定义控件添加Panel子控件,并将该控件公开。同时将属性的DesignerSerializationVisibility特性设置为DesignerSerializationVisibility.Content.
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace NestedControlDesignerLibrary
{
/// <summary>
/// A test Control to demonstrate allowing nested Controls
/// to accept child controls at design time.
/// </summary>
[
Designer(typeof(NestedControlDesignerLibrary.Designers.TestControlDesigner))
]
public partial class TestControl : UserControl
{
public TestControl()
{
InitializeComponent();
}
#region TestControl PROPERTIES ..........................
/// <summary>
/// Surface the Caption to allow the user to
/// change it
/// </summary>
[
Category("Appearance"),
DefaultValue(typeof(String), "Test Control")
]
public string Caption
{
get
{
return this.lblCaption.Text;
}
set
{
this.lblCaption.Text = value;
}
}
[
Category("Appearance"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
]
/// <summary>
/// This property is essential to allowing the designer to work and
/// the DesignerSerializationVisibility Attribute (above) is essential
/// in allowing serialization to take place at design time.
/// </summary>
public Panel WorkingArea
{
get
{
return this.pnlWorkingArea;
}
}
#endregion
}
}
2. 自定义设计器。看上面代码中的UserControl的特性。
{
public class TestControlDesigner : ParentControlDesigner
{
public override void Initialize(System.ComponentModel.IComponent component)
{
base.Initialize(component);
if (this.Control is TestControl)
{
this.EnableDesignMode((
(TestControl)this.Control).WorkingArea, "WorkingArea");
}
}
}
}
通过使用EnableDesignMode可以指定要公开的子容器,具体可以看该方法的介绍。如果要添加多个子容器,将其他的都写在这里就好了。
3. 最后的问题,子容器控件被公开,意味着我们可以再设计时更改其属性。虽然也不是完全没用,但很多时候会给人带来困扰,或者改变了原本的组合关系,变的乱糟糟,因此有必要隐藏部分属性,如Dock等。Minute也提到了一些方法。
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ImprovedNestedControlDesignerLibrary
{
[
Designer(typeof(ImprovedNestedControlDesignerLibrary.Designers.WorkingAreaDesigner))
]
public partial class WorkingAreaControl : Panel
{
public WorkingAreaControl()
{
InitializeComponent();
base.Dock = DockStyle.Fill;
}
}
}
就是通过将子容器从Panel继承,在子容器的设计器里面屏蔽Dock属性。
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms.Design;
namespace ImprovedNestedControlDesignerLibrary.Designers
{
public class WorkingAreaDesigner : ScrollableControlDesigner
{
protected override void PreFilterProperties(
System.Collections.IDictionary properties)
{
properties.Remove("Dock");
base.PreFilterProperties(properties);
}
}
}
designerTypeName 是提供设计时服务的设计器类型的完全合格的名称。传递 designerTypeName 参数的System.Windows.Forms.Design.ParentControlDesigner 和 System.Design 的组合。ParentControlDesigner 类扩展了 UserControl 的设计时行为。
designerBaseType 是设计器的基类的名称。用于设计时服务的类必须实现 IDesigner 接口。
将 UserControl 创建为设计时控件容器
- 创建一个新的 Visual C# .NET Windows 控件库项目。为此,请按照下列步骤操作:
- 启动 Visual Studio .NET。
- 在“文件”菜单上,指向“新建”,然后单击“项目”。
- 在“项目类型”下,单击 “Visual C# 项目”,然后单击“模板”下的 “Windows 控件库”。
- 将该项目命名为 ContainerUserControl。默认情况下将创建出 “UserControl1.cs”。
- 在解决方案资源管理器中,右键单击 “UserControl1.cs”,然后单击“查看代码”。
- 将下面的代码添加到 Declarations 部分:
using System.ComponentModel.Design;
- 如下所示将 System.ComponentModel.DesignerAttribute 属性应用到该控件:
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))] public class UserControl1 :System.Windows.Forms.UserControl { ... }
- 在“生成”菜单上,单击“生成解决方案”。