继前两篇文章winform 程序对界面上控件的统一控制【二】组件版和winform 程序对界面上控件的统一控制【一】(赋值\清空\验证……),本篇文章我要实现的是重写ComboBoxEdit(Dev控件),定义自己的数据绑定和显示样式。(donet的ComboBox控件同理可得)
先看看重写ComboBoxEdit所提出的需求:
(1)少量结构化的数据需要填充到ComboBoxEdit的Items集合中,在窗体画面上,我们看到的是"index - value" 的形式,但是真正取的值是Index。这种功能也就是donet中的ComboBox控件提供的DisplayMember和ValueMember属性,可以实现这种表现值和实际值不一样的功能。
(2)这种少量结构化数据是不存储在数据中也不存储在文件中,如果每个ComboBoxEdit控件我们都去设置Items属性,则会很麻烦, 因为这样的数据很多。
如:提供的端数处理区分数据为:5 切舍,6 四舍五入,7 进位。实现的效果如图1所示(中间加"-"是项目中规定的):
图1 图2 图3
看看我所重写的ComboBoxEdit的组件:
{
/// <summary>
/// 组件构造函数
/// </summary>
public UCComboBox()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
this.Properties.AutoComplete = true;
this.Properties.ImmediatePopup = true;
this.Properties.TextEditStyle = DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor;
}
/// <summary>
/// 构造
/// </summary>
/// <param name="container"></param>
public UCComboBox(IContainer container)
{
container.Add(this);
InitializeComponent();
Initialize();
}
private string _itemValue = "";
/// <summary>
/// ComboBoxEdit选中值对应的索引
/// </summary>
[Browsable(false)]
public string ItemValue
{
get
{
return GetIndex(this._controlType.ToString(), this.Text);
}
set
{
_itemValue = value;
if (!DesignMode)
{
this.Text = this.GetComplexValueByIndex(_itemValue);
}
}
}
private string _itemDisplay = "";
/// <summary>
/// ComboBoxEdit选中的值
/// </summary>
[Browsable(false)]
public string ItemDisplay
{
get
{
return GetValue(this._controlType.ToString(), this.Text);
}
set { _itemDisplay = value; }
}
private CtrlType _controlType = CtrlType.默认;
/// <summary>
/// 控件类型属性
/// </summary>
[Description("选择控件类型"), Category("自定义")]
[DefaultValue(typeof(CtrlType), "默认")]
public CtrlType ControlType
{
get { return _controlType; }
set
{
_controlType = value;
if (!this.DesignMode)
{
if (_controlType != CtrlType.默认)
{
FillCombItem(_controlType.ToString());
}
}
}
}
private bool _emptyItem = false;
/// <summary>
/// 是否包含空行
/// </summary>
[Description("是否含有空行,非必录下拉框有空行,必录的下拉框不要加空行"), Category("自定义")]
[DefaultValue(typeof(bool), "false")]
public bool AEmptyItem
{
get
{
return _emptyItem;
}
set
{
_emptyItem = value;
if (!DesignMode && _emptyItem)
{
this.Properties.Items.Add("");
}
}
}
/// <summary>
/// 填充下拉框Item
/// </summary>
/// <param name="_ctrlname"></param>
private void FillCombItem(string _ctrlname)
{
Dictionary<string, string> dicItem = EnumFactory.EnumToTable(_ctrlname);
foreach (KeyValuePair<string, string> kvp in dicItem)
{
this.Properties.Items.Add(string.Format("{0} - {1}", kvp.Key, kvp.Value));
}
}
private List<EnumItem> _enumlist = new List<EnumItem>();
/// <summary>
/// 自定义枚举类型
/// </summary>
[Category("自定义")]
[Browsable(true)]
[Description("特殊的枚举,自定义枚举键值"),
ReadOnlyAttribute(false)]
public List<EnumItem> EnumList
{
get
{
return _enumlist;
}
set
{
if (_controlType == CtrlType.默认 && _enumlist.Count != 0 && !this.DesignMode)
{
_enumlist = value;
FillCombItem(_enumlist);
}
}
}
/// <summary>
/// 填充下拉框Item
/// </summary>
/// <param name="ctrlEnum"></param>
private void FillCombItem(List<EnumItem> ctrlEnum)
{
ctrlEnum.ForEach(enumitem =>
{
this.Properties.Items.Add(enumitem.EnumText);
});
}
/// <summary>
/// 获取comboboxEdit当前值所对应的索引值
/// </summary>
/// <param name="_ctrlname">控件类型</param>
/// <param name="content">当前选取值</param>
/// <returns></returns>
private string GetIndex(string _ctrlname, string content)
{
int start = content.IndexOf("-");
try
{
if (start > 0)
{
return content.Split('-')[0].Trim();
}
else
return GetIndexByValue(content);
}
catch (System.Exception err)
{
throw err;
}
}
/// <summary>
/// 获取comboboxEdit当前值所对应的值
/// </summary>
/// <param name="_ctrlname">控件类型</param>
/// <param name="content">当前选取值</param>
/// <returns></returns>
private string GetValue(string _ctrlname, string content)
{
int start = content.IndexOf("-");
try
{
if (start > 0)
{
return content.Split('-')[1].Trim();
}
else
return content;
}
catch (System.Exception err)
{
throw err;
}
}
/// <summary>
/// 通过Enum的索引获取对应的值
/// 无对应值是返回为空
/// </summary>
/// <param name="index">索引</param>
/// <returns></returns>
public string GetValueByIndex(string index)
{
try
{
foreach (string _item in this.Properties.Items)
{
int start = _item.IndexOf("-");
if (start > 0)
{
string[] arr = _item.Split('-');
if (arr[0].Trim().Equals(index))
{
return arr[1].Trim();
}
}
}
return "";
}
catch (System.Exception err)
{
throw err;
}
}
/// <summary>
/// 通过Enum的索引获取对应的值,形如:1-是
/// 无对应值是返回为空
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public string GetComplexValueByIndex(string index)
{
try
{
foreach (string _item in this.Properties.Items)
{
int start = _item.IndexOf("-");
if (start > 0)
{
string[] arr = _item.Split('-');
if (arr[0].Trim().Equals(index))
{
return _item;
}
}
}
return "";
}
catch (System.Exception err)
{
throw err;
}
}
/// <summary>
/// 获取comboboxEdit当前值所对应的索引值
/// 无对应值是返回为空
/// </summary>
/// <param name="content">当前选取值</param>
/// <returns></returns>
public string GetIndexByValue(string content)
{
try
{
foreach (string _item in this.Properties.Items)
{
int start = _item.IndexOf("-");
if (start > 0)
{
string[] arr = _item.Split('-');
if (arr[1].Trim().Equals(content))
{
return arr[0].Trim();
}
}
}
return "";
}
catch (System.Exception err)
{
throw err;
}
}
/// <summary>
/// 获取comboboxEdit当前值所对应的索引值
/// 无对应值是返回为空
/// </summary>
/// <param name="content">当前选取值,形如:1-是</param>
/// <returns></returns>
public string GetIndexByComplexValue(string content)
{
try
{
int start = content.IndexOf("-");
if (start > 0)
{
return content.Split('-')[0].Trim();
}
return "";
}
catch (System.Exception err)
{
throw err;
}
}
}
说明:(1)FillCombItem方法通过EnumFactory.EnumToTable方法,将选择的枚举以"index - value"的方式添加ComboBoxEdit的Items集合当中。
(2)ControlType属性,类型为CtrlType枚举,将在文章后面定义。控件初始化的时候会根据该属性自动填充数据到Items集合中。
(3)AEmptyItem属性,Bool类型,指示该控件的下拉列表中是否包含空的Item,也就是表明该项可为空。效果如图2所示。
(4)EnumList属性,类型为List<EnumItem>,EnumItem类型会在文章后面定义,定义该属性是为了一些特殊的情况,我们都知道,枚举声明只可以显式地声明 byte、sbyte、short、ushort、int、uint、long 或 ulong 类型作为对应的基础类型,那么这样就会出现一些情况,比如P 生产日期型,C 消费期限型或者是 002 已生成,003 已删除,005已审核,006 已作废,009 已部分付款,012 已完成 ;这类结构化的数据,就不能用枚举来表示了,所以必须用另外的实现方式。我的处理方式是,定义一个EnumItem的实体类型,要求能被序列化。ValueMember属性相当于index,DisplayMember属性想到与value,EnumText属性就是"index - value"了。如图3所示。
(5)定义了一些获取当前选中值的Index和Vlaue值之类的方法和属性。
EnumItem类型定义如下:
/// 自定义枚举类
/// </summary>
[Serializable]
public class EnumItem
{
private string _valueMember = string.Empty;
/// <summary>
/// 枚举索引值
/// </summary>
[Description("枚举索引值"),
ReadOnlyAttribute(false)]
public string ValueMember
{
get
{
return _valueMember;
}
set
{
if (!string.IsNullOrEmpty(value))
{
_valueMember = value;
SetText();
}
}
}
private string _displayMember = string.Empty;
/// <summary>
/// 枚举值
/// </summary>
[Description("枚举值"),
ReadOnlyAttribute(false)]
public string DisplayMember
{
get
{
return _displayMember;
}
set
{
if (!string.IsNullOrEmpty(value))
{
_displayMember = value;
SetText();
}
}
}
void SetText()
{
_enumText = _valueMember + "-" + _displayMember;
}
private string _enumText = string.Empty;
/// <summary>
/// 显示值形式
/// ValueMember-DisplayMember
/// </summary>
[Description("值浏览"),
ReadOnlyAttribute(true)]
public string EnumText
{
get
{
return _enumText;
}
set
{
_enumText = value;
}
}
}
CtrlType枚举类型定义如下:
/// 控件类型
/// </summary>
public enum CtrlType
{
默认,
端数处理区分,
打印订货台账
}
最后一步,我通过反射去查找相应的枚举,并把枚举值以Dictionary<string, string>的形式返回。
/// 枚举操作工厂类
/// </summary>
public class EnumFactory
{
static string assemblyName = "RealSailing.BizControls";
private static Enum CreateEnumFactory(string enumName)
{
string className = "RealSailing.UI.Common.EnumFactory+" + enumName;
return (Enum)Assembly.Load(assemblyName).CreateInstance(className);
}
/// <summary>
/// Enum转化为Dictionary,供数据控件绑定用
/// </summary>
/// <param name="enumName">枚举类型名称</param>
/// <returns>将Enum转化后的Dictionary</returns>
public static Dictionary<string, string> EnumToTable(string enumName)
{
Dictionary<string, string> dicItem = new Dictionary<string, string>();
Enum eu = CreateEnumFactory(enumName);
Type oper = eu.GetType();
foreach (string enumValue in Enum.GetNames(oper))
{
string enumIndex = Enum.Format(oper, Enum.Parse(oper, enumValue), "d");
dicItem.Add(enumIndex, enumValue);
}
return dicItem;
}
#region 枚举
public enum 端数处理区分
{
切舍 = 5,
四舍五入 = 6,
进位 = 7
}
public enum 指定店打印台账
{
不打印 = 0,
打印 = 1,
指定店打印 = 2
}
}
在这个反射工厂中,我们特别需要注意CreateEnumFactory方法,对于内嵌在类中的枚举,我们反射的时候需要注意:反射内嵌类需要使用"+"而不是我们常使用的"." 。如在类EnumFactory的CreateEnumFactory方法中,是这样定义要反射的对象名称:
string className = "RealSailing.UI.Common.EnumFactory+" + enumName;
而非:string className = "RealSailing.UI.Common.EnumFactory." + enumName;
使用:(1)对于普通的数据,我们可以定义枚举,先在CtrlType枚举中添加枚举名,然后在将该枚举添加到class EnumFactory当中。这样就很方便扩展,而且很通用了。只需要对UCComboBox控件CtrlType类型的属性ControlType选择即可运行时填充数据到Items属性中。如图4示:
图4
(2)对于比较特殊的数据,我们可以设置其EnumList属性,同样可以到达效果。如图5示:
图5
运行后,最终的实现效果如图1、2、3所示。这就是我想表达的,不知道这会真的清楚了没有?来信来函免费索取源码!
写完就回家去了,祝各位园友虎年大吉,虎虎生威!
提供源码下载地址:http://cid-09a0ee26b2c7fbe3.office.live.com/self.aspx/.Public/Common.rar
包括前两篇blog中的控件源码。