我在“ 数组详论”与“Enum详论”两篇文章中曾经介绍过如何将数组以及 Enum 项目当作系结控件的数据来源。然而,无论是数组或是 Enum 项目, 它们都有设定和使用上的巨大限制。其中, Enum 的每个项目都必须是常值, 换句话说, 项目的值无法动能控制。而数组的值虽然可以动态变更, 但无法提供设计时的 Intellisense 支持...
我在“ 数组详论”与“Enum详论”两篇文章中曾经介绍过如何将数组以及 Enum 项目当作系结控件的数据来源。然而,无论是数组或是 Enum 项目, 它们都有设定和使用上的巨大限制。其中, Enum 的每个项目都必须是常值, 换句话说, 项目的值无法动能控制。而数组的值虽然可以动态变更, 但无法提供设计时的 Intellisense 支持。
建立自订类
如果你想综合所有的优点, 你就必须使用自订类, 如下例:
public class clsPeriodForQuery
{
?? public int 今天 { get { return 0; } }
?? public int 今天跟昨天 { get { return 1; } }
?? public int 最近三天 { get { return 2; } }
?? public int 这个礼拜 { get { return (int)DateTime.Now.DayOfWeek; } }
?? public int 最近两个礼拜 { get { return 这个礼拜 + 7; } }
?? public int 最近三个礼拜 { get { return 这个礼拜 + 14; } }
?? public int 最近四个礼拜 { get { return 这个礼拜 + 21; } }
?? public int 这个月 { get { return DateTime.Now.Day - 1; } }
?? public int 最近两个月
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = today - new DateTime(today.Year, today.AddMonths(-1).Month, 1);
?????????? return span.Duration().Days - 1;
?????? }
?? }
?? public int 最近半年
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = today - new DateTime(today.Year, today.AddMonths(-6).Month, 1);
?????????? return span.Duration().Days;
?????? }
?? }
?? public int 今年 { get { return DateTime.Now.DayOfYear - 1; } }
?? public int 最近一年
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = (new DateTime(today.Year, 12, 31) - new DateTime(today.Year, 1, 1));
?????????? return span.Duration().Days - 1;
?????? }
?? }
}
以上这个类内含“今天”、“今天跟昨天”、“最近三天”、“这个礼拜”、“最近两个礼拜”、“最近三个礼拜”、“最近四个礼拜”、“这个月”、“最近两个月”、“最近半年”、“今年”与“最近一年”等项目, 其值就是距离今天的天数。如同你可以看见的, 在以上各个项目中, 有些是常值(Constant), 有些必须经过运算才能得到。像这种结构, 你无法以 Enum 来表示。当然你可以改用数组或集合(Collection)对象, 但是在设计时期却无法枚举。但如果设计成自订类, 就可以达成所有的功能。
实践 IListSource 以作为数据来源
但是, 一般的自订类是无法直接提供给系结控件作为数据来源的。要让一个自订类当作数据来源, 你的类必须实践 IListSource、IEnumerable 或 IDataSource 才行。实践 IListSource 大概是最简单、最容易的; 以下我展示一个已经写好可用的范例:
using System.Reflection;
using System.ComponentModel;
...
public class clsPeriods :? IListSource
{
?? public int 今天 { get { return 0; } }
?? public int 今天跟昨天 { get { return 1; } }
?? public int 最近三天 { get { return 2; } }
?? public int 这个礼拜 { get { return (int)DateTime.Now.DayOfWeek; } }
?? public int 最近两个礼拜 { get { return 这个礼拜 + 7; } }
?? public int 最近三个礼拜 { get { return 这个礼拜 + 14; } }
?? public int 最近四个礼拜 { get { return 这个礼拜 + 21; } }
?? public int 这个月 { get { return DateTime.Now.Day - 1; } }
?? public int 最近两个月
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = today - new DateTime(today.Year, today.AddMonths(-1).Month, 1);
?????????? return span.Duration().Days - 1;
?????? }
?? }
?? public int 最近半年
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = today - new DateTime(today.Year, today.AddMonths(-6).Month, 1);
?????????? return span.Duration().Days;
?????? }
?? }
?? public int 今年 { get { return DateTime.Now.DayOfYear - 1; } }
?? public int 最近一年
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = (new DateTime(today.Year, 12, 31) - new DateTime(today.Year, 1, 1));
?????????? return span.Duration().Days - 1;
?????? }
?? }
?? public clsPeriods() { } // Constructor
?? bool IListSource.ContainsListCollection
?? {
?????? get { return false; }
?? }
?? IList IListSource.GetList()
?? {
?????? BindingList list = new BindingList();
?????? PropertyInfo[] pInfos = typeof(clsPeriods).GetProperties();
?????? foreach (PropertyInfo info in pInfos)
?????? {
?????????? string text = info.Name;
?????????? clsPeriodscls = new clsPeriods();
?????????? object obj = info.GetValue(cls, null);
?????????? int value = 0;
?????????? if (int.TryParse(obj.ToString(), out value))
?????????????? list.Add(new pair(text, value));
?????? }
?????? return (IList) list;
?? }
}
public class pair
{
?? string _Key;
?? int _Value;
?? public string Key
?? {
?????? get { return _Key; }
?????? set { _Key = value; }
?? }
?? public int Value
?? {
?????? get { return _Value; }
?????? set { _Value = value; }
?? }
?? public pair() { }
?? public pair(string k, int v) {
?????? Key = k;
?????? Value = v;
?? }
}
在上例中, 我必须另外建立名为 pair 的自订类, 它只是一个单纯用来存放 Key/Value Pair 的容器而已。在 VS2008/.Net Framework 3.5 中, 这种类的写法可以再简化一点:
public class pair
{
?? public string Key { set; get; } // Only on VS2008
?? public int Value { set; get; } // Only on VS2008
?? public pair() { }
?? public pair(string k, int v) {
?????? Key = k;
?????? Value = v;
?? }
}
此外, 为了在执行时期取出设计时期加入的类资讯 (在这里指的是类的 Properties 项目资讯), 所以我们必须用到 Reflection 命名空间下的方法, 所以请记得加上 using System.Reflection; 命令。同时, 为了实践 IListSource, 我们也必须引用 ComponentModel 命名空间。
实践 IListSource 界面时, 你必须同时实践 ContainsListCollection 这个 Property, 并实践 GetList() 方法以返回一个 IList 实例, 如范例中所示。
现在, 我们已经能将这个自订类当作系结控件的数据来源了:
clsPeriods cls = new clsPeriods();
ddlPeriods.DataSource = cls;
ddlPeriods.DataTextField = "Key";
ddlPeriods.DataValueField = "Value";
ddlPeriods.DataBind();
透过这种方式, 我们就能够使用自订类以取代 Enum 以提供可以动态变更内容的系结来源。
更简单的做法
在上面的范例中, 为了提供像 DropDownList 之类具有 TextField 与 ValueField 的控件以作为数据来源, 我们又另外制作了称为 pair 的自订类型。从好的方向想, 这种做法提供了很大的弹性, 你可以以这个模型为基础, 继续发展更多的应用。不过话说回来, 我原来只是想稍为增强 Enum 功能而已, 我并不想因为这个目的, 而需要特别去维护一个类型。我难道不能简单的使用 .Net 已经提供的既有类型吗?
.Net 有很多很好用的集合类型可以使用, 如果你不想自己创建一个自订类型的话, 我们可以从现有的 .Net 类型中挑选一个来使用。以下是使用 SortedList 类型的范例:
using System.Reflection;
...
public class clsPeriods
{
?? public int 今天 { get { return 0; } }
?? public int 今天跟昨天 { get { return 1; } }
?? public int 最近三天 { get { return 2; } }
?? public int 这个礼拜 { get { return (int)DateTime.Now.DayOfWeek; } }
?? public int 最近两个礼拜 { get { return 这个礼拜 + 7; } }
?? public int 最近三个礼拜 { get { return 这个礼拜 + 14; } }
?? public int 最近四个礼拜 { get { return 这个礼拜 + 21; } }
?? public int 这个月 { get { return DateTime.Now.Day - 1; } }
?? public int 最近两个月
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = today - new DateTime(today.Year, today.AddMonths(-1).Month, 1);
?????????? return span.Duration().Days - 1;
?????? }
?? }
?? public int 最近半年
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = today - new DateTime(today.Year, today.AddMonths(-6).Month, 1);
?????????? return span.Duration().Days;
?????? }
?? }
?? public int 今年 { get { return DateTime.Now.DayOfYear - 1; } }
?? public int 最近一年
?? {
?????? get
?????? {
?????????? DateTime today = DateTime.Now;
?????????? TimeSpan span = (new DateTime(today.Year, 12, 31) - new DateTime(today.Year, 1, 1));
?????????? return span.Duration().Days - 1;
?????? }
?? }
}
public SortedList slPeriods
{
?? get
?? {
?????? SortedList list = new SortedList();
?????? PropertyInfo[] pInfos = typeof(clsPeriodForQuery).GetProperties();
?????? foreach (PropertyInfo info in pInfos)
?????? {
?????????? string text = info.Name;
?????????? clsPeriodForQuery cls = new clsPeriodForQuery();
?????????? object value = info.GetValue(cls, null);
?????????? list.Add(value, text);
?????? }
?????? return list;
?? }
}
在上面范例中, 我只是很简单的使用 Reflection 命名空间下的功能, 把 clsPeriods 的 Properties 撷取出来并填入一个 SortedList 对象并回传而已。由于使用的是 SortedList 对象, 我们取出枚举数值的时候, 它都是自动排列的。所以这个范例和上个范例不同, 我们取到的值都是按天数多寡排过顺序的。同时, 由于我在塞入 SortedList 的 Text 与 Value 字段对调过了, 所以它被用作数据系结控件的数据来源时, DropDownList 的字段也必须对调:
ddlPeriods.DataSource = slPeriods;
ddlPeriods.DataTextField = "Value";
ddlPeriods.DataValueField = "Key";
ddlPeriods.DataBind();
如果你不希望排序的话, 你可以把 SortedList 类型改成 HashTable 类型, 它就不会排序了。
总结
以上两个范例都用在 TextField 与 ValueField 不一样的场合。如果你不需要这样做, 你可以无需自订 pair 类型, 使用字符串即可。.Net 就是这样, 对于同样一种需求, 你可能可以使用两百种做法来实现, 就看你最熟悉哪种做法, 或是哪种做法最有效率。
一般而言, 写死在程序里面的东西效率通常最好, 但是也最没有弹性; 如果可以写进数据库, 那么它的效率最低, 但是很容易修改。在本文范例中, 其实你也可以把列表由数据库直接产生(使用 Stored Procedure 来撰写程序逻辑), 然后使用 ADO.NET 工具读取出来并填入容器对象。但如此一来, 如果数据系结动作很频繁, 难免会影响到性能。
折衷之道, 就是如范例般写在自订类里面。这个自订类可以放在外部的类库(Class Library)项目里面, 如果有更改, 也不会导致网站项目需要重新编译, 如此弹性也会比较大。
Dev 2Share @ 点部落
原文:大专栏 自订类的属性与字段如何枚举并提供控件作为系结来源?
原文地址:https://www.cnblogs.com/petewell/p/11510010.html