前言
在需要录入数据的字段比较多的表单应用程序中,为了给用户更好的体验,我们通常会将[Enter]键转为[TAB]将输入焦点移到下一个控件,或是将获得焦点的输入控件背景经一个醒目的背景颜色显示等等。以往的做法通常是从TextBox、ComboBox等标准输入控件派生一个新的控件,在新控件中改变击键和在获得/失去焦点时的动作,但此方法的不便之外就是到项目的最后,会增加了一系列的标准控件的小功能扩展控件,增大了后期的维护工作量。在DotNet中,对于类似的对标准控件的“小功能扩展”我们有了更好解决方案,那就是神奇的IExtenderProvider接口,它可以给任何控件“变”出一个属性来^_^
关于IExtenderProvider 接口
IExtenderProvider位于System.ComponentModel命名空间,其声明如下:
public
interface
IExtenderProvider {
bool
CanExtend(
object
extendee); }
此接口只有一个方法bool CanExtend(object)
,这个方法告诉VS.net设计器是否将扩展属性显示在给出控件的属性窗口中。实现了IExtenderProvider接口的组件称为“扩展程序提供程序”。DotNet 2.0中自带的ToolTips和ErrorProvider就是二个典型的扩展程序提供程序。如将ToolTips控件添加到窗体中后,TextBox1控件便多了个ToolTips on toolTips1的属性。

我们在属性窗口中给此属性设置值时,VS.net设计器生成如下的代码:
this
.toolTip1.SetToolTip(
this
.textBox1,
"
some tooltips
"
);
可见,我们给ToolTips on toolTips1属性设置的文本是存储在toolTips1控件中,但是程序执行时是在textBox1上显示相应的提示信息而不是在toolTips1上。这样,我们无需对窗体上现有的控件做任何修改就可以让他们都有了toolTips提示功能!
接下来,我们来创建一个自己的扩展程序提供程序,给TextBox控件添加[ConverEnterToTAB]功能。
创建自己的IExtenderProvider 组件
这时候我们注意到IExtenderProvider接口
并没有提供任何有关扩展属性的消息,那vs.net设计器是如何知道扩展程序提供什么样的扩展属性给控件呢?是ProvideProperty特性(Attribute)!设计器会在实现IExtenderProvider
接口的组件中寻找所有的ProvideProperty
特性,ProvideProperty
特性标明了扩展程序给什么类型的控件提供什么扩展扩展属性的信息。ProvideProperty特性的声明如下:
[AttributeUsageAttribute(AttributeTargets.Class, AllowMultiple
=
true
)]
public
sealed
class
ProvidePropertyAttribute : Attribute
此特性有二个构造方法:
public
ProvidePropertyAttribute (
string
propertyName,
//
扩展到指定类型对象的属性名称。
string
receiverTypeName
//
此属性可以扩展的数据类型的名称。
)
--------------------------------------------
public
ProvidePropertyAttribute (
string
propertyName,
//
扩展到指定类型对象的属性名称。
Type receiverType
//
可以接收此属性的对象的数据类型的 Type。
)
在ProvideProperty特性中指定提供的属性的名称和可接收属性的组件类型,如,下面代码给TextBox类型组件添加一个名为<Name>的属性:
[ProvideProperty(
"
<Name>
"
,
typeof
(TextBox))]
public
class
TextboxExtender : Component, IExtenderProvider {...
一旦在控件上声明了ProvideProperty特性,就需要在控件中提供名为Get<Name>和Set<Name>的二个Public方法。Get<Name>方法有一个参数,参数类型和ProvideProperty特性中定义的类型相同,返回此组件此属性的值。Set<Name>方法带两个参数,第一个参数是ProvideProperty特性中指定的组件类型,第二个参数是要设置给扩展属性的数据,类型和Get<Name>方法返回的数据类型相同。
下列是给TextBox添加EnableConvertEnterToTab扩展属性的完整代码:
namespace
wuChang.Controls {
///
<summary>
///
将TextBox的回车键转换为TAB键
///
wuChang@guet.edu.cn
///
wuchang.cnblogs.com
///
</summary>
[ProvideProperty(
"
EnableConvertEnterToTab
"
,
typeof
(TextBox))]
public
class
TextboxExtender : Component, IExtenderProvider {
private
Hashtable htDate
=
new
Hashtable();
///
<summary>
///
取得相应组件EnableConvertEnterToTab扩展属性的值
///
</summary>
///
<param name="control"></param>
///
<returns></returns>
[DefaultValue(
false
)] [Description(
"
是否允许将回车键转换为TAB键
"
)]
public
bool
GetEnableConvertEnterToTab(TextBox control) {
return
this
.htDate.Contains(control); }
///
<summary>
///
将需要转换Enter的控件保存到Hashtable中,前添加KeyDown事件。
///
</summary>
///
<param name="control"></param>
///
<param name="value"></param>
public
void
SetEnableConvertEnterToTab(TextBox control,
bool
value) {
if
(value) {
this
.htDate.Add(control, value); control.KeyDown
+=
new
KeyEventHandler(control_KeyDown); }
else
{ control.KeyDown
-=
new
KeyEventHandler(control_KeyDown);
this
.htDate.Remove(control); } }
///
<summary>
///
将目标控件的键盘事件{Enter}转为{TAB}
///
</summary>
///
<param name="sender"></param>
///
<param name="e"></param>
void
control_KeyDown(
object
sender, KeyEventArgs e) {
if
(e.KeyCode
==
Keys.Enter) { e.Handled
=
true
; SendKeys.Send(
"
{TAB}
"
); } }
#region
IExtenderProvider Members
public
bool
CanExtend(
object
extendee) {
return
extendee
is
TextBox; }
#endregion
} }
注意这里给给GetEnableConvertEnterToTab方法添加[DefaultValue(false)]特性,这样如果给组件的GetEnableConvertEnterToTab扩展属性设计值为False时,设计器将不会生成设置代码。如设置扩展属性为true时会生成这样的代码:
this.textboxExtender1.SetEnableConvertEnterToTab(this.textBox1, true);
设置为Falsh时,设计器不会生成任何的代码。

参考资料:
http://www.codeproject.com/dotnet/iextenderprovider.asp
ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_fxdeveloping/html/21e34929-ff78-4f3f-aee8-a16a4ef5ac9d.htm