• 利用override多态原理实现对相似页面的后台代码的抽象,并实现动态GridView动态列数据绑定


    关于多态本人写过一篇随笔,您兴趣的可以看一下 http://www.cnblogs.com/FreeDong/archive/2012/08/07/2626312.html

    以下通过一个ASP.NET的Demo,希望能使您加深对多态的理解。

    现在的需求是这样子(当然该需求是借助于最近的项目中碰到的问题),在该系统中的流程管理中,有两个页面,一个显示的是我本人发起的审批列表,另一个是等待我进行审批的列表,他们的查询以及列表显示和查看审批历史等均一致,唯一不同的是待审批还有一个可执行审批动作的一列,但是不同的人或者在不同的应用(我在这里假设该系统是有多个应用的复杂系统)里面获取到列表的列是不一样的,所以需要使用动态列,当然这些数据来源我在这里不赘述,为便于举例我也不会去使用到数据库,您可以构造一个类,包含一个属性为列的集合,一个属性为数据集即可,当然下面的例子均会涉及。

    我的环境为Windows 7+ Visual Studio 2010,我创建了一个WebApplication,名为WebApplication3,为了页面好看点,我将不会选择空Web Application。添加两个aspx页面(MyApprovals和ToApprovals),并在母版页的导航添加链接(如果你没有母版页可以跳过此步骤直接创建页面即可)

    View Code
    <asp:Menu ID="NavigationMenu" runat="server" CssClass="menu" EnableViewState="false" IncludeStyleBlock="false" Orientation="Horizontal">
                        <Items>
                            <asp:MenuItem NavigateUrl="~/Default.aspx" Text="主页"/>
                            <asp:MenuItem NavigateUrl="~/MyApprovals.aspx" Text="我的申请"/>
                            <asp:MenuItem NavigateUrl="~/ToApprovals.aspx" Text="待我审批"/>
                            <asp:MenuItem NavigateUrl="~/About.aspx" Text="关于"/>
                        </Items>
                    </asp:Menu>

    页面代码很简单,MyApprovals的页面代码如下:

    View Code
    <p>
        <asp:TextBox ID="txtKey" runat="server"></asp:TextBox>&nbsp;
        <asp:Button ID="btnSearch" runat="server" Text="Button" 
            onclick="btnSearch_Click" />
        <asp:Label ID="lblMsg" runat="server" ForeColor="Red"></asp:Label>
    </p>
    <div>
        <asp:GridView ID="gvApprovals" runat="server">
        </asp:GridView>
    </div>

    ToApprovals的页面代码后面再贴出来。

    创建他们的后台代码的基类名为ApprovalBasePage,我们在实际开发中可能还有一个更BasePage之类的基类,这时ApprovalBasePage就应继承于BasePage,

    public class BasePage : System.Web.UI.Page {  }
    View Code
        public class ApprovalBasePage : BasePage
        {
            /// <summary>
            /// 0为我发起审批的页面,1为待审批的页面,可由子类去确定类型
            /// </summary>
            protected virtual int PageType { get { return 0; } }
    
            private ApprovalData dataSource;
            /// <summary>
            /// 获取数据源
            /// </summary>
            ApprovalData DataSource
            {
                get
                {
                    if (this.dataSource == null)
                    {
                        this.dataSource = ApprovalData.GetData(this.PageType);
                    }
                    return this.dataSource;
                }
            }
    
            /// <summary>
            /// 进行数据绑定,参数仅作为示范作用,
            /// 因为绑定时可能会与用户有些交互,这些交互很可能不相同,因而留给子类去重写
            /// 但是本例中他们的数据绑定几乎是一样的
            /// </summary>
            protected virtual void BindData(string key = ""){ }
    
            /// <summary>
            /// 将数据转化为DataTable,便于绑定
            /// </summary>
            /// <returns></returns>
            protected DataTable DataToTable()
            {
                DataTable dt = new DataTable();
                if (this.DataSource == null)
                {
                    return null;
                }
    
                foreach (var item in this.DataSource.HeadList)
                {
                    dt.Columns.Add(item, typeof(string));
                }
    
                DataRow dr = null;
                int colIndex = 0;
                foreach (var item in this.DataSource.ContentList)
                {
                    dr = dt.NewRow();
                    colIndex = 0;
                    foreach (var itemCol in item)
                    {
                        dr[colIndex++] = itemCol;
                        if (colIndex >= dt.Columns.Count)
                        {
                            //虽然提供该接口的保证数据肯定是与列数对应的
                            //但我还是尽量保证数据异常时程序不会报错
                            break;
                        }
                    }
                    dt.Rows.Add(dr);
                }
    
                return dt;
            }
    
            #region event 两个页面的共同事件都放进这里来了
    
            protected void Page_Load(object sender, EventArgs e)
            {
                if (!IsPostBack)
                {
                    BindData();
                }
            }
    
            protected void btnSearch_Click(object sender, EventArgs e)
            {
                BindData();
            }
            #endregion
        }

    我这里的BasePage什么都不做(注意,我并不喜欢把Web项目的C#程序写在App_code文件夹,因为我从我刚开始接触ASP.NET我就讨厌它)。还有,您会发现ApprovalBasePage类中的数据来源来自于类ApprovalData,这只是我构造出来的提供数据的类,那么在实际需求当中它可能是有更底层的接口实现的,并且这个接口可能都不是由我们来编写,我甚至都可以不用管这些数据,因为可能其逻辑是极其复杂,我只需要知道它给我提供的数据集的结构即可,这就是面向对象的抽象思维,提供下这个类的代码,其中有json数据,用于绑定“操作”列的下拉菜单。

    View Code
        public class ApprovalData
        {
            /// <summary>
            /// 数据列表头部集合
            /// </summary>
            public List<string> HeadList { get; set; }
    
            /// <summary>
            /// 包含的数据集,以行为里层集合,外层的集合为行的集合
            /// </summary>
            public List<List<string>> ContentList { get; set;}
            
            public ApprovalData()
            {
                this.HeadList = new List<string>();
                this.ContentList = new List<List<string>>();
            }
    
            /// <summary>
            /// 模拟返回数据的方法,这里构造的是写死的数据
            /// </summary>
            /// <param name="dataType">0为我发起的审批数据,1为待我审批的数据</param>
            /// <returns></returns>
            public static ApprovalData GetData(int dataType)
            {
                ApprovalData model = new ApprovalData();
                model.HeadList.AddRange(new string[]{"标题","发起时间","状态"});
                if (dataType == 1)
                {
                    model.HeadList.Add("操作");
                    model.ContentList.AddRange(
                        new List<string>[] { 
                            new List<string> { "项目启动申请","2012-08-01","已提交","[{key:2,value:'同意'},{key:3,value:'拒绝'}]" },
                            new List<string> { "请批准报销","2011-12-23","已提交","[{key:4,value:'数据有错'},{key:4,value:'通过并提交给总监'}]" }
                        });
                }
                else
                {
                    model.ContentList.AddRange(
                        new List<string>[] { 
                            new List<string> { "请批准经费","2012-08-01","未提交" },
                            new List<string> { "请假申请","2011-12-23","已批准" }
                        });
                }
    
                return model;
            }
        }

    OK他们的基类准备完毕,接下来编写MyApprovals.aspx.cs

    View Code
        public partial class MyApprovals : ApprovalBasePage 
        {
            protected override void BindData(string key = "")
            {
                DataTable dt = DataToTable();
                if (dt == null)
                {
                    this.lblMsg.Text = "No Record";
                    return;
                }
                this.gvApprovals.DataSource = dt;
                this.gvApprovals.DataBind();
            }
        }

    好了,先浏览下这个页面的效果:

    很难看哦,很抱歉我可没有任何美化能力,如果您需要美化她,可以通过css。这个页面正常,接下来还有个难点在于待审批页面。ToApprovals页面的Gridview中有一列,是操作,表示我可以进行的操作,我在这里只示范绑定效果,提交操作的程序,如果您需要,我认为问题不大。

    首先为ToApprovals的GridView添加一个模板列,页眉为“操作”,转到编辑模板页,添加DropDownList和HiddenField,并绑定HiddenField,如下图所示

    其最终页面代码为:

    View Code
    <p>
        <asp:TextBox ID="txtKey" runat="server"></asp:TextBox>&nbsp;
        <asp:Button ID="btnSearch" runat="server" Text="Button" 
            onclick="btnSearch_Click" />
        <asp:Label ID="lblMsg" runat="server" ForeColor="Red"></asp:Label>
    </p>
    <div>
        <asp:GridView ID="gvApprovals" runat="server" AutoGenerateColumns="False" 
            onrowdatabound="gvApprovals_RowDataBound">
            <Columns>
                <asp:TemplateField HeaderText="操作">
                    <ItemTemplate>
                        <asp:DropDownList ID="DropDownList1" runat="server">
                        </asp:DropDownList>
                        <asp:HiddenField ID="hdf" runat="server" Value='<%# Eval("操作") %>' />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </div>

    编写后台代码,这里我不再使用自动生成列,由我编码生成列,

    View Code
        public partial class ToApprovals : ApprovalBasePage
        {
            protected override int PageType
            {
                get
                {
                    return 1;
                }
            }
    
            protected override void BindData(string key = "")
            {
                DataTable dt = DataToTable();
                if (dt == null)
                {
                    this.lblMsg.Text = "No Record";
                    return;
                }
                this.gvApprovals.DataSource = dt;
    
                string head = string.Empty;
                int count = dt.Columns.Count;
                //因为模板列已存在,所以要进行倒序插入第一列
                for (int i = count - 1; i >= 0; i--)
                {
                    var item = dt.Columns[i].ColumnName;
                    //注意这一项我们约定好是操作,如可变性大,可以写在配置文件
                    if (item == "操作" || item == null)
                    {
                        //因为该列已写在模板列,所以不在这里生成列
                        continue;
                    }
                    BoundField field = new BoundField();
                    field.HeaderText = item;
                    field.DataField = item;
                    this.gvApprovals.Columns.Insert(0, field);
                }
                this.gvApprovals.DataBind();
            }
        }

    完成以上代码编写后运行,页面已正常显示,但是操作里面的下拉菜单还是空的,没有数据,这里我还要在做一件事,就是绑定其,我们要使用到GridView的RowDataBound事件,该事件代码如下,添加至ToApprovals.aspx.cs。

    View Code
            protected void gvApprovals_RowDataBound(object sender, GridViewRowEventArgs e)
            {
                HiddenField hdf = e.Row.FindControl("hdf") as HiddenField;
                DropDownList ddl = e.Row.FindControl("DropDownList1") as DropDownList;
                if (hdf == null || string.IsNullOrEmpty(hdf.Value) || ddl == null)
                {
                    return;
                }
    
                JavaScriptSerializer jss = new JavaScriptSerializer();
                KeyValue[] obj = jss.Deserialize<KeyValue[]>(hdf.Value);
    
                ddl.DataSource = obj;
                ddl.DataTextField = "value";
                ddl.DataValueField = "key";
                ddl.DataBind();
            }

    还有一个数据类型是KeyValue,这是我自己定义的

    View Code
        /// <summary>
        /// 类似键值对,用于绑定
        /// </summary>
        public struct KeyValue
        {
            public string key { get; set; }
            public string value { get; set; }
        }

    然后运行效果

    这里可能有人问了,为什么不用Dictionary,原因是我提供的json数据不能直接转化为该类型,我们一般解析json格式的字符串时,定义一个类或者结构,然后在转化,如上代码所示,很简单吧?是的,当人的思想上升到一定境界之后不管碰到什么问题,我们总会找到解决方案,编程更是如此,没有满足不了的需求,只有我们还没想到解决方案。

    其实在页面的实现方式上还有一个实现方式,即嵌套循环显示列表,这种方式也有其便捷之处,在ASP.NET MVC下可以使用该方法。本例主要也是为了讲解下动态使用GridView的方式,当然您也可以直接用C#编程生成有一个Gridview,然后在添加到页面中。

    好累丫,终于写完,感觉我上面讲的好乱啊,如果有幸能让您一直看完,我对您表示衷心的感谢。还是附上源码吧 http://download.csdn.net/detail/dongdong22014/4492932



     感谢阅读,请留下您的意见或疑问! 能力有限,错漏难免,欢迎指点!

     分割线:我的个人原创,请认准 http://freedong.cnblogs.com/ (转摘不标原文出处可耻)

  • 相关阅读:
    setlocale
    c++的 程序实体 作用域 名空间 变量生存期概念理解
    本人的Ubuntu 10.04配置清单
    hadoopStreamming 编程 Angels
    级联 mapreduce (Cascading Mapreduce) Angels
    委托
    OPC和XML/SOAP/Web Services
    实例管理2(会话服务)
    实例管理3()
    操作(Operation)
  • 原文地址:https://www.cnblogs.com/FreeDong/p/2627198.html
Copyright © 2020-2023  润新知