Ext.Net 1.2.0_Ext.Net.GridPanel 实现多级Grid

实现多级 Grid 在 Ext.Net Demo 里有,本文旨在进一步说明它的实现,以及在此基础上,说明如何在多级Grid上,进行增删改等操作。

本文内容

  • 多级 Grid 概述
  • 实现多级 Grid

多级 Grid 概述

有这样一个实现:Grid 数据的某行下,也包含数据,单击或双击时,需要展开。默认情况,Grid 只显示第一级,当点击某行记录最前边的“+”,或双击某行记录时,展开与该记录相关的数据。对于数据库来说,最一般的是——自连表。

在暂不考虑数据分页的前提下,问问自己:

  • 首先,假设我们处于 Ajax 时代之前,没有任何三方组件可以提供这个功能,你对 JavaScript 只有一定的了解,但是对 C# 或是 Java 很熟练,那么若想实现这个功能你能想到什么?
  • 其次,假设我们处于 Ajax 时代之前,没有任何三方组件可以提供这个功能,但你已经对 JSON 和 JavaScript 都很熟悉,那么若想实现这个功能你能想到什么?
  • 最后,假设我们已经处于 Ajax 时代,没有现成的三方组件可以很好地提供这个功能,但是你对 JSON 和 jQuery 很熟悉,那么若想实现这个功能你能想到什么?

对于第一个假设,因为没有异步、没有局部刷新,我们只能将数据全部从数据库读出来,因为你对JavaScript 还不甚了解,所以只能利用 C# 或 JavaScrit 在服务器端创建嵌套的 <div> 或是 <table>(这需要一个相对复杂的逻辑),还要为 Grid 的展开创建客户端代码,绝大多数操作都在服务器端——这个实现绝不简单。要命的是,效率实在太低,暂且不说它完全需要回发,用户体验也差到极致。

对于第二个假设,没有异步、没有局部刷新,更没有现成三方组件可用,但是你已对 JSON 和 JavaScrit 有很好的了解,此时,你能想到的,也许是,从服务器端把数据全部读出来,因为数据库表本身就是自连表(比如,有 ID,有 PARENTID),无需进行复杂的业务处理,只需要将数据转换成 JSON 格式,剩下的工作,完全在客户端完成(利用 JavaScript 解析 JSON)——这个也不大容易。但至少比第一个强,客户端完成绝大多数操作。

对于第三个假设,有异步、有局部刷新,更关键的是你对 JSON 和 jQuery 都很熟悉,那完全可以从数据库先获得第一级的数据,当用户单击或双击时,利用 jQuery,发出 Ajax 请求,从数据库获得指定记录下的相关数据,然后在回调成功时,添加到指定记录下……——这个思路不仅清晰,而且实现相对简单,用户体验也好,都是局部刷新,效率也高。

其实,说了这么多,仅仅是锻炼你的思维。因为现在很多三方组件都提供这个功能。如果你能体会人家的设计思想,以及实现一个功能时的思路,使用三方组件其实很容易,虽然看上去很复杂。

实现多级 Grid

首先看看,客户端代码都有什么,如下所示:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ExtNetMultiGridPanel._Default" %>
 
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="Script" />
 
    <script type="text/javascript">
   1:  
   2:         window.lookup = {};
   3:  
   4:         var params = {
   5:             action: " ",
   6:             ui: gridUIParams,
   7:             setAction: function(action) {
   8:                 this.action = action;
   9:             },
  10:             setParams: function(recId, level) {
  11:                 if (arguments.length == 2) {
  12:                     if (this.ui == undefined) {
  13:                         this.ui = gridUIParams;
  14:                     }
  15:                     this.ui.id = recId;
  16:                     this.ui.level = level;
  17:                 }
  18:                 else if (arguments.length == 0) {
  19:                     if (this.ui == undefined) {
  20:                         this.ui = gridUIParams;
  21:                     }
  22:                     this.ui.id = " ";
  23:                     this.ui.level = " ";
  24:                 }
  25:             },
  26:             toJson: function() {
  27:                 return Ext.encode(this);
  28:             }
  29:         };
  30:  
  31:         var gridUIParams = {
  32:             id: "",
  33:             level: ""
  34:         };
  35:  
  36:         var selectedRow = function(recId, level, obj) {
  37:             params.setParams(recId, level);
  38:             obj.setValue(params.toJson());
  39:         };
  40:  
  41:         var setAction = function(action, obj) {
  42:             var paras = Ext.decode(obj.getValue());
  43:             params.action = action;
  44:             if (paras.ui.id != undefined) params.ui.id = paras.ui.id;
  45:             if (paras.ui.level != undefined) params.ui.level = paras.ui.level;
  46:             obj.setValue(params.toJson());
  47:         };
  48:  
  49:         var clean = function(view, isDestroy) {
  50:             var controls = window.lookup[view.grid.id] || {},
  51:                 ids = [];
  52:  
  53:             for (var c in controls) {
  54:                 ids.push(controls[c].id || controls[c].storeId);
  55:             }
  56:  
  57:             if (ids.length > 0) {
  58:                 if (isDestroy !== true) {
  59:                     view.grid.getRowExpander().collapseAll();
  60:                 }
  61:  
  62:                 for (var i = 0; i < ids.length; i++) {
  63:                     removeFromCache(ids[i], view.grid.id);
  64:                 }
  65:             }
  66:         };
  67:  
  68:         var addToCache = function(c, parent) {
  69:             window.lookup[parent] = window.lookup[parent] || {};
  70:             window.lookup[parent][c] = window[c];
  71:         };
  72:  
  73:         var removeFromCache = function(c, parent) {
  74:             window.lookup[parent] = window.lookup[parent] || {};
  75:  
  76:             var ctrl = window.lookup[parent][c];
  77:             delete window.lookup[parent][c];
  78:             if (ctrl) {
  79:                 if (ctrl.view) {
  80:                     clean(ctrl.view, true);
  81:                 }
  82:                 ctrl.destroy();
  83:             }
  84:         };
  85:  
  86:         var loadLevel = function(expander, record, body, row) {
  87:             if (body.rendered) {
  88:                 return;
  89:             }
  90:             var recId = record.id,
  91:                 gridId = expander.grid.id,
  92:                 level = record.data.Level;
  93:             Ext.net.DirectMethods.BuildGrid(level + 1, recId, gridId, {
  94:                 eventMask: {
  95:                     showMask: true,
  96:                     tartget: "customtarget",
  97:                     customTarget: expander.grid.body
  98:                 },
  99:  
 100:                 success: function() {
 101:                     body.rendered = true;
 102:                 },
 103:  
 104:                 failure: function() {
 105:                     alert('服务器错误.');
 106:                 }
 107:             });
 108:         };
 109:  
 110:         Ext.onReady(function() {
 111:             var obj = Ext.getCmp("Params");
 112:             params.setParams();
 113:             obj.setValue(params.toJson());
 114:         });
 115:     
</script>
 
</head>
<body>
    <form id="form1" runat="server">
    <ext:ResourceManager ID="ResourceManager1" runat="server" />
    <ext:Toolbar ID="Toolbar1" runat="server">
        <Items>
            <ext:Toolbar ID="Toolbar2" runat="server">
                <Items>
                    <ext:Button ID="Button1" runat="server" Text="添加">
                        <Listeners>
                            <Click Handler="setAction('add',#{Params});" />
                        </Listeners>
                        <DirectEvents>
                            <Click OnEvent="btn_action_Click">
                                <ExtraParams>
                                    <ext:Parameter Name="action" Value="Ext.decode(#{Params}.getValue()).action" Mode="Raw">
                                    </ext:Parameter>
                                    <ext:Parameter Name="id" Value="Ext.decode(#{Params}.getValue()).ui.id" Mode="Raw">
                                    </ext:Parameter>
                                </ExtraParams>
                            </Click>
                        </DirectEvents>
                    </ext:Button>
                    <ext:Button ID="Button2" runat="server" Text="修改">
                        <Listeners>
                            <Click Handler="setAction('edit',#{Params});" />
                        </Listeners>
                        <DirectEvents>
                            <Click OnEvent="btn_action_Click">
                                <ExtraParams>
                                    <ext:Parameter Name="action" Value="Ext.decode(#{Params}.getValue()).action" Mode="Raw">
                                    </ext:Parameter>
                                    <ext:Parameter Name="id" Value="Ext.decode(#{Params}.getValue()).ui.id" Mode="Raw">
                                    </ext:Parameter>
                                </ExtraParams>
                            </Click>
                        </DirectEvents>
                    </ext:Button>
                    <ext:Button ID="Button3" runat="server" Text="删除">
                        <Listeners>
                            <Click Handler="setAction('del',#{Params});" />
                        </Listeners>
                        <DirectEvents>
                            <Click OnEvent="btn_action_Click">
                                <ExtraParams>
                                    <ext:Parameter Name="action" Value="Ext.decode(#{Params}.getValue()).action" Mode="Raw">
                                    </ext:Parameter>
                                    <ext:Parameter Name="id" Value="Ext.decode(#{Params}.getValue()).ui.id" Mode="Raw">
                                    </ext:Parameter>
                                </ExtraParams>
                            </Click>
                        </DirectEvents>
                    </ext:Button>
                </Items>
            </ext:Toolbar>
        </Items>
    </ext:Toolbar>
    <ext:Hidden ID="Params" runat="server" Text="">
    </ext:Hidden>
    </form>
</body>
</html>

说明:

1,自定义两个类:params 和 gridUIParams,用来保存从Grid上获得的相关数据,如当前选择的行ID等,已经当前页面要进行的操作,如add、del 和 edit。

2,定义两个脚本方法 selectedRow 和 setAction,分别用来将从Grid上获得的相关信息,保存到隐藏域;以及将当前页面的操作保存到隐藏域。

3,onReady 里的代码,用来初始化隐藏域的内容。

4,方法 clean、addToCache、removeFromCache 和 loadLevel 是创建多级 Grid 的相关客户端方法。其中,loadLevel 方法通过Ext.net.DirectMethods,直接调用服务器端方法,以异步方式创建多级 Grid。

接下来,看看后台代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using Ext.Net;
using Ext.Net.Utilities;
 
namespace ExtNetMultiGridPanel
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!X.IsAjaxRequest)
            {
                this.MultiLevel = 4;
            }
            else
            {
                return;
            }
            this.BuildGrid(1, "r0", "r0");
        }
        protected void btn_action_Click(object sender, DirectEventArgs e)
        {
            string id = string.Empty;
            string action = string.Empty;
            if (e.ExtraParams["id"] != null) id = e.ExtraParams["id"].Trim().ToString();
            if (e.ExtraParams["action"] != null) action = e.ExtraParams["action"].Trim().ToString();
 
 
            string currentAction = string.Empty;
            switch (action)
            {
                case "add": currentAction = "添加"; break;
                case "edit": currentAction = "编辑"; break;
                case "del": currentAction = "删除"; break;
            }
            string currentSelect = id.Trim().Length > 0 ? "选择行ID=" + id : "未选择行。";
            X.Js.Alert("操作:" + currentAction + ";" + currentSelect);
            // TODO
        }
        [DirectMethod]
        public void BuildGrid(int level, string recId, string gridId)
        {
            DataTable dt = DS.GetDataSource();
            DataSet ds = new DataSet();
            if (level == 1)
            {
                ds.Merge(dt.Select("parentid=-1"));
                dt = ds.Tables[0];
            }
            else
            {
                DataRow[] drs = dt.Select("parentid=" + recId);
                if (drs.Length > 0)
                {
                    ds.Merge(drs);
                    dt = ds.Tables[0];
                }
                else { dt = new DataTable(); }
 
            }
 
            if (dt != null && dt.Rows.Count > 0)
            {
                var storeId = "L".ConcatWith(level, "_", recId, "_Store");
                var newGridId = "L".ConcatWith(level, "_", recId, "_Grid");
 
                // 创建  Store
                var store = new Store { ID = storeId };
                var reader = new JsonReader
                {
                    IDProperty = "ID",
                    Fields = { new RecordField("ID", RecordFieldType.Int), 
                        new RecordField("PARENTID", RecordFieldType.Int), 
                        new RecordField("NAME", RecordFieldType.String) }
                };
 
                reader.Fields.Add(new RecordField
                {
                    Name = "Level",
                    Convert = { Handler = "return ".ConcatWith(level, ";") }
                });
                store.Reader.Add(reader);
                store.CustomConfig.Add(new ConfigItem("level", level.ToString(), ParameterMode.Raw));
 
                // 创建  GridPanel
                var grid = new GridPanel
                {
                    ID = newGridId,
                    Store = { 
                                    store
                                 },
                    AutoHeight = true,
                    HideHeaders = false,
                    ColumnModel =
                    {
                        Columns = { new Ext.Net.Column { Header = "标识", DataIndex = "ID" },
                                    new Ext.Net.Column {Header="父标识",DataIndex="PARENTID"},
                                    new Ext.Net.Column {Header="名称",DataIndex="NAME"}
                        }
                    }
                };
                // 创建  GridView
                var view = new Ext.Net.GridView
                {
                    ID = newGridId + "_View",
                    ForceFit = true
                };
                grid.View.Add(view);
 
                // 创建 RowSelectionModel
                var sm = new RowSelectionModel { ID = newGridId + "_SM", SingleSelect = true };
                sm.Listeners.RowSelect.Handler = "selectedRow(" + "record.id," + level + ",#{Params});";
                grid.SelectionModel.Add(sm);
 
                // 创建 BeforeExpand, except last (last level is 5)
                if (level <= this.MultiLevel)
                {
                    view.Listeners.BeforeRefresh.Fn = "clean";
                    var re = new RowExpander
                    {
                        ID = newGridId + "_RE",
                        EnableCaching = true,
                        Template = { ID = newGridId + "_TPL", Html = "<div id=\"row" + level + "_{ID}\" style=\"background-color:white;\"></div>" }
                    };
 
                    re.Listeners.BeforeExpand.Fn = "loadLevel";
 
                    grid.Plugins.Add(re);
                }
 
                store.DataSource = dt;
                store.DataBind();
 
                if (level == 1)
                {
                    grid.AutoHeight = true;
                    grid.AutoWidth = true;
                    this.Form.Controls.Add(grid);
 
                    grid.Plugins.Add(new PanelResizer());
                }
                else
                {
                    var renderEl = "row" + (level - 1) + "_" + recId;
                    X.Get(renderEl).SwallowEvent(new string[] { "click", "mousedown", "mouseup", "dblclick" }, true);
 
                    this.RemoveFromCache(newGridId, gridId);
                    grid.Render(renderEl, RenderMode.RenderTo);
                    this.AddToCache(newGridId, gridId);
                }
            }
        }
        /// <summary>
        /// GridPanel 的级别
        /// </summary>
        /// <remarks>
        /// 默认值为1,最大为4。即 GridPanel 默认为1级,最大4级
        /// </remarks>
        protected int MultiLevel
        {
            get
            {
                if (this.ViewState["MultiLevel"] != null)
                {
                    if (Convert.ToInt16(this.ViewState["MultiLevel"].ToString()) <= 4)
                        return Convert.ToInt16(this.ViewState["MultiLevel"].ToString());
                    else return 4;
                }
                return 1;
            }
            set
            {
                this.ViewState["MultiLevel"] = value;
            }
        }
        /// <summary>
        /// 清除缓存
        /// </summary>
        /// <param name="id">子GridPanel的ID</param>
        /// <param name="parentId">父GridPanel的ID</param>
        private void RemoveFromCache(string id, string parentId)
        {
            X.ResourceManager.AddScript("removeFromCache({0}, {1});", JSON.Serialize(id), JSON.Serialize(parentId));
        }
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="id">子GridPanel的ID</param>
        /// <param name="parentId">父GridPanel的ID</param>
        private void AddToCache(string id, string parentId)
        {
            X.ResourceManager.AddScript("addToCache({0}, {1});", JSON.Serialize(id), JSON.Serialize(parentId));
        }
    }
}

下载 Demo

原文地址:https://www.cnblogs.com/liuning8023/p/2197175.html