在Windows Form编程中,如果我们希望实现一个textbox的扩展,例如加入自定义属性或方法,很容易想到的是使用继承自textbox的自定义控件。但是这样做有时会出现一些问题,比如仅仅为了添加一个额外属性就要创建一个的自定义控件是否开销过大?某些继承出来的控件不支持在视图设计器中进行编辑等等。.Net中我们可以使用另一种方法来实现扩展控件的目的,这就是通过IExtenderProvider接口。通过它我们可以向容器中的其他控件提供额外的属性和扩展方法。比较直观的例子就是Tooltip组件,它就继承了IExtenderProvider接口。将它拖拽进一个容器内时就会发现,容器中所有的控件的属性中都新增了“toolTip1上的tooltip”这么一项属性。类似于这样就可以不必自定义控件也能扩展额外属性了。下面是我写的一个简单的例子:
using System.ComponentModel;
using System.Collections;
using System.Windows.Forms;
namespace ClassLibrary1
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
/// <summary>
/// Component1 的摘要说明。
/// </summary>
[ProvideProperty("Name", typeof(Control))]
public class Component1 : System.ComponentModel.Component ,IExtenderProvider
{
private Hashtable _innerTable = new Hashtable();
private System.ComponentModel.Container components = null;
private XmlDataDocument xmlDataDoc = new XmlDataDocument();
public Component1(System.ComponentModel.IContainer container)
{
InitializeComponent();
}
[DefaultValue("")]
public string GetName(Control control)
{
string text = (string)_innerTable[control];
if (text == null)
{
text = string.Empty;
}
return text;
}
public void SetName(Control c1, string v1)
{
if(_innerTable.Contains(c1))
{
_innerTable[c1]=v1;
}
else
{
_innerTable.Add(c1,v1);
}
c1.KeyPress += new KeyPressEventHandler(c1_KeyPress);
}
public XmlDataDocument XmlDataDoc
{
set
{
xmlDataDoc = value;
}
get
{
return xmlDataDoc;
}
}
public Component1()
{
///
/// Windows.Forms 类撰写设计器支持所必需的
///
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
组件设计器生成的代码
IExtenderProvider 成员
private void c1_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar == (char)13)
{
SendKeys.Send("{TAB}");
}
}
}
}
我们可以将额外需要的属性填写在这里,同时我为控件添加了一个回车键跳转,当按回车键时就好像按了TAB键一样。我们既不用自定义这些控件,又不用在容器中添加触发,只要将这些都写到继承了IExtenderProvider接口的组件中就OK了,而对于需要这些属性和方法的控件来说不需要任何的改变。
{
// TODO: 添加 Component1.CanExtend 实现
if(extendee is Control && !(extendee is Component1))
{
return true;
}
else
{
return false;
}
}
这个方法是IExtenderProvider接口的成员。通过修改它就可以设置我们究竟要为哪种控件添加属性。我这里选择的是Control,所有的控件都会返回true。
Name即是我需要的属性名称。当然这个Name不同于控件本身的Name属性。
public string GetName(Control control)
{
string text = (string)_innerTable[control];
if (text == null)
{
text = string.Empty;
}
return text;
}
public void SetName(Control c1, string v1)
{
if(_innerTable.Contains(c1))
{
_innerTable[c1]=v1;
}
else
{
_innerTable.Add(c1,v1);
}
c1.KeyPress += new KeyPressEventHandler(c1_KeyPress);
}
通过GetName和SetName这两个方法即可对扩展的属性内容进行操作。_innerTable是一个HashTable。在Get的事后可以像属性一样为其设置默认值,但是Set是不可以的。另外在Set时除了设置其值外还可以添加事件。
使用这种方法我们可以减少不必要的自定义控件的数目,移植起来也更加方便。我原来参与的项目中自定义控件有40多个,其中一部分就属于添加一个属性就要一个自定义控件的情况,因为一个属性,就要为其自定义对应的textbox、combobox、checkbox等等,让人觉得很是不甘心... 通过这种方法就可以给所有符合要求的控件添加同一的属性,管理起来也很便捷。
这种方法的缺点就是没有继承来得灵活,如果添加的属性不是“大而全”的那种或者有比较复杂的控制关系,还是继承控件比较好用一些吧