说起扩展编程组件,我们不得不起到一个接口IExtenderProvider,参考msdn的定义:定义将属性扩展到容器中的其他组件的接口;下面我贴上IExtenderProvider的定义:
/// <summary>
/// 定义将属性扩展到容器中的其他组件的接口。
/// </summary>
public interface IExtenderProvider
{
/// <summary>
/// 指定此对象是否可以将其扩展程序属性提供给指定的对象。
/// </summary>
/// <param name="extendee">要接收扩展程序属性的System.Object。</param>
/// <returns>如果此对象可以扩展程序属性提供给指定对象,则为true;否则为false。</returns>
bool CanExtend(object extendee);
}
根据定义我们可以理解为,实现了IExtenderProvider接口的某个控件或者组件有扩展其他组件的能力,IExtenderProvider接口借助VisualStudio设计器的功能,使我们可以动态的为控件增加属性,并可以在设计时通过属性窗口修改这些属性,而不需要继承控件类。这样做的最大好处是可以借助Visual Studio的能力,并保持控件原有的设计时支持。
IExtenderProvider接口组件提供了一种与容器中其他组件的粘合能力,这无疑是为我们提供了另一种组件的扩展思想,我理解为侵入编程,假设没有这个方式,我们扩展一个组件需求继承原有的组件并且增加一个或多个扩展属性,比如我们要达到这么一个目的,界面上指定的方本模框必须输入字符才能离开输入焦点,如果我们用传统方法,需求重写TextBox控件,代码如下:
public class TextBoxEx:TextBox
{
protected override void OnDragLeave(EventArgs e)
{
if (this.allowInput)
{
if(this.Text.Trim().Length ==0)
{
MessageBox.Show("文本框必须输入字符!", "提示", MessageBoxButtons.OK,
MessageBoxIcon.Information);
if (this.CanFocus)
this.Focus();
}
base.OnDragLeave(e);
}
}
/// <summary>
/// 是否容器输入为空。
/// </summary>
[Category("验证")]
[Description("获取/设置是否必须输入")]
[DefaultValue(false)]
[Browsable(true)]
public bool AllowInput
{
get
{
return this.allowInput;
}
set
{
this.allowInput = value;
}
}
/// <summary>
/// 默认必须输入字符。
/// </summary>
private bool allowInput = false;
}
我们来在UI上拖一个TextBoxEx控件,可以通过设置属性AllowEmpty来控件是否允许输入空值:
以下这种方式可以理解为.NET的原生实现方式,需求继续原来控件,并且在应用过程上需要调用UI的控件,比较麻烦,下面我们来用IExtenderProvider做一个实现:
[ToolboxItem(true)]
[Description("TextBox必须输入组件。")]
[ProvideProperty("AllowInput", typeof(TextBox))]
public partial class TextBoxAllowInput : Component, System.ComponentModel.IExtenderProvider
{
private Dictionary<TextBox, bool> allowInputList = null;
public TextBoxAllowInput()
{
InitializeComponent();
allowInputList = new Dictionary<TextBox, bool>();
}
public TextBoxAllowInput(IContainer container)
{
container.Add(this);
InitializeComponent();
allowInputList = new Dictionary<TextBox, bool>();
}
[Category("输入验证")]
[Description("获取/设置文本框是否必须输入")]
public bool GetAllowInput(TextBox textBox)
{
if (allowInputList.ContainsKey(textBox))
{
return (bool)allowInputList[textBox];
}
return false;
}
public void SetAllowInput(TextBox textBox, bool allowInput)
{
if (!allowInputList.ContainsKey(textBox))
{
allowInputList.Add(textBox, allowInput);
textBox.Leave += new EventHandler(textBox_Leave);
}
else
{
allowInputList[textBox] = allowInput;
}
}
void textBox_Leave(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
if (this.GetAllowInput(tb))
{
if (tb.Text.Trim().Length == 0)
{
MessageBox.Show("文本框必须输入字符!", "提示", MessageBoxButtons.OK,
MessageBoxIcon.Information);
if (tb.CanFocus)
tb.Focus();
}
}
}
public bool CanExtend(object extendee)
{
return (extendee is TextBox); //只扩展TextBox
}
}
我们来在UI上拖一个几个TextBox控件,再拖一个TextBoxAllowInput 组件,我们可以通过设置TextBox的在TextBoxAllowInput 上的扩展属性AllowEmpty来控件是否允许输入空值:
上例简单的介绍了一下IExtenderProvider接口及其背后的编程思想,在开发过程中,我们可以应用这种侵入式编程接口实现很多的扩展组件如数据绑定、输入验证、焦点跳转、输入法切换等多种多样的快捷编程辅助组件。
下面我将会介绍基于IExtenderProvider扩展技术的UI控件与实体的数据绑定组件。