鲜荣彬
Herry

最近,看着公司自定义的控件,觉得自己应该学习这些东西,这样有助于自己基础知识的巩固,于是试着还原公司的代码,看自己是否有这样的功力。

  公司的控件大致有这样的用途:将控件放一个容器中,通过给自己定义控件绑定数据库中表的字段,然后根据业务的需求自动生成增删改查Sql语句,程序员可以将重点放在业务的分析处理中,尽可能的少写SQL语句。先不说这样的方法好不好,抱着学习的思想,我于是试着还原这些代码。

  现在想一下实现如此的功能:在自定义的一个容器控件中,放入自定义控件,有TextBox,Label,CheckBox等,然后,点击Button,自动生成AddSql、UpdateSql、SearchSql语句,然后直接执行这些语句,减少Sql语句的编写量,加快项目进展(这里Sql语句优化不考虑)。

  首先,控件开始考虑吧,我这里以TextBox为例子阐述自己的思路。

  对于TextBox,大家估计都比较熟悉,转到定义后可以看见

  TextBox : WebControl, IPostBackDataHandler, IEditableTextControl, ITextControl

  TextBox控件继承了一个WebControl类,实现了三个接口,非常漂亮的设计。

  WebControl类,微软官方的解释为:提供所有 Web 服务器控件的公共属性、方法和事件。通过设置在此类中定义的属性,可以控制 Web 服务器控件的外观和行为。由此可见,Web服务器控件应该都会继承或则个类了喔。

  为了防止Sql注入,微软强烈建议我们使用带参数的Sql语句,即如果是Sql Server 数据库,参数名应该是"@Name",如果是Oracle数据库,参数名应该是":Name"(似乎微软自身似乎不支持My Sql,如果支持的话,应该是"?")。因此,自动生成的AddSql与UpdateSql语句中,应该包括字段名,字段值;自动生成的SearchSql语句,应该更加复杂点,我们手写Sql语句时,会写 Name='刘德华' 或 Name like '%刘' 或 Name like '刘%' 或 Name like '%刘%',因此需要判断匹配符所在的位置。

  分析到现在,我们的xTextBox(自定义控件的名字)额外的属性有字段名、字段类型、查询类型、填充类型。

  首先定义查询类型类,因为类型是确定的,所以用枚举,代码如下

    public enum QueryTag
    {
        Exact,//精准查询 Name='刘德华'
        Dark,//模糊查询 Name like '%刘%'
        Start,//%前置模糊查询 Name like '%刘'
        End//%后置模糊查询 Name like '刘%'
    }

 现在分析,如何保存这些中间值(因为这些值本身不是太重要,我们需要得到的是处理后的值),这是值得考虑的。我们在学习三层架构的的时候,大都会把一个表的字段放在一个Model类中,这样处理的好处是我们只要得到一个Model,就可以从中取得我们需要的任一个值。因此,定义一个Field类,保存用以保存获取自定义控件的属性的值。那么自定义控件可能需要什么额外的属性呢?默认填充值得类型、数据库字段名、数据库字段值、查询标记。代码如下

  那么,现在有一个问题就是如何在容器中区分wTextBox与TextBox?

  其实,最先考虑的是根据类型名来区分

  如果asp:TextBox控件的ID为 txtASP,则txtASP.GetType().Name的值为TextBox

      如果wTextBox的ID为txtW,则txtW.GetType().Name为wTextBox

  如果我区分控件,则需要如下书写

foreach (Control ctrl in testPanel.Controls)
           {
               switch (ctrl.GetType().Name)
               {
                   case "wTextBox":
                       break;
                   case "wLable":
                       break;
                   case "wCheckBox":
                       break;
           ......
} }

  这种写法虽然可以很好的区分自定义控件以及微软的服务器控件,假设有10个自定义控件,则要10个case语句,原则上不好,因此,寻找另外一种方法。

  那么,我就参考TextBox实现方式,即实现某一个接口,因此可以让所有的自定义控件实现这个接口,那么,实现了自定义接口的控件便是自定义控件。问题解决了,不用具体区分是wTextBox还是wLable了。这里,我定义一个接口IBoundControl,让所有自定义的控件都去实现它。那么这个接口应该有什么功能呢。首先,这个接口可以得到一个自定义控件所有的属性,即可以得到Field类,其次,我们在进行数据填充的时候,需要给控件赋值,则需要一个方法,给控件赋值的方法。Field代码代码如下

public class Field
    {
        // Fields
        public FillType FillType;//默认填充值
        public DisplayFormat Format;//现实形式 普通字符串 还是日期格式等
        public string Key;//数据库表的字段名 字段名 Name
        public string Name;//数据库表的字段名 字段名 Name
        public QueryTag QueryTag;//查询标记 生成查询语句用
        public DbType Type;//字段类型 
        public object Value;//数据库表的字段传递的值 字段值 张柏芝

        // Methods
        public Field()
        {
            this.Name = string.Empty;
            this.Key = string.Empty;
            this.Value = string.Empty;
            this.Type = DbType.String;
            this.QueryTag = QueryTag.Exact;
            this.Format = DisplayFormat.Normal;
            this.FillType = FillType.Null;
        }
        public Field(string strName, object strValue)
        {
            this.Name = strName;
            this.Value = strValue;
        }
        public Field(string strName, object strValue, DbType fieldType)
        {
            this.Type = fieldType;
        }
        public Field(string strName, string strKey, object strValue)
        {
            this.Key = strKey;
        }
        public Field(string strName, string strKey, object strValue, DbType fieldType)
        {
            this.Type = fieldType;
        }
        public override string ToString()
        {
            if (string.IsNullOrEmpty(this.Key))
            {
                return this.Name;
            }
            return (this.Name + "-" + this.Key);
        }
    }
IBoundControl接口代码如下:
 public interface IBoundControl
 {
   Field GetBoundField();
    void SetValue(string strValue);
 }

  此时,xTextBox代码已经出来了,代码如下

 

public class xTextBox:TextBox,IBoundControl
    {
        #region 属性描述
        [DefaultValue(""), Description("绑定的字段名称")]
        public string wFieldName
        {
            get
            {
                return (this.ViewState["wFieldName"] as string);
            }
            set
            {
                this.ViewState["wFieldName"] = value;
            }
        }

        [DefaultValue("String"), Description("绑定的字段类型")]
        public DbType wFieldType
        {
            get
            {
                string str = this.ViewState["wFieldType"] as string;
                return (DbType)Enum.Parse(typeof(DbType), str);
            }
            set
            {
                this.ViewState["wFieldType"] = value.ToString();
            }
        }

        [DefaultValue("Null"), Description("填充类型")]
        public FillType wFillType
        {
            get
            {
                string str = this.ViewState["wFillType"] as string;
                return (FillType)Enum.Parse(typeof(FillType), str);
            }
            set
            {
                this.ViewState["wFillType"] = value.ToString();
            }
        }

        [Description("字段查询标记"), DefaultValue("Exact")]
        public QueryTag wQueryTag
        {
            get
            {
                string str = this.ViewState["wQueryTag"] as string;
                return (QueryTag)Enum.Parse(typeof(QueryTag), str);
            }
            set
            {
                this.ViewState["wQueryTag"] = value.ToString();
            }
        } 
        #endregion

        public xTextBox()
        {
            this.wFieldName = "";
            this.wFieldType = DbType.String;
            this.wQueryTag = QueryTag.Exact;
            this.wFillType = FillType.Null;
        }

        public Field GetBoundField()
        {
            return new Field { 
                Name = this.wFieldName,
                Value = this.Text,
                Type = this.wFieldType,
                QueryTag = this.wQueryTag,
                FillType = this.wFillType };
        }

        public void SetValue(string strValue)
        {
            this.Text = strValue;
        }
    }

 

  前面已经讲过了,我们是把控件放在一个特定的容器,然后在在这个容器中读取数据的,那么容器的唯一需要的功能便是读取自定义控件。当然为了区别asp:Panel,我们仍需要定义一个接口,让容器控件实现这个接口,接口的功能只有一个,得到所有自定义控件。

public interface IBoundContainder
    {
        IList<IBoundControl> GetControls();
    }
public class xPanel:Panel,IBoundContainder
    {
        public IList<IBoundControl> GetControls()
        {
            return this.GetControls(this);
        }

        public IList<IBoundControl> GetControls(Control container)
        {
            List<IBoundControl> list = new List<IBoundControl>();
            foreach (Control control in container.Controls)
            {
                if (control.HasControls())
                {
                    list.AddRange(this.GetControls(control));
                }
                else if (control is IBoundControl)
                {
                    list.Add((IBoundControl)control);
                }
            }
            return list;
        }
    }

到此,xPanel与xTextBox已经完成,接下来会讲解如何利用自定义控件根据业务需求去完成增、改、查、显示功能。

posted on 2013-01-26 16:44  Herry彬  阅读(2028)  评论(2编辑  收藏  举报