zoukankan      html  css  js  c++  java
  • Ext.Net-Grid 篇

    概述

    前两篇分别介绍了Ext.NET-基础Ext.NET-布局,从本篇开始我们尽量做一些实际工作中用到的例子。

    在Ext.NET官方示例中,关于GridPanel的例子是最多的(近百个),篇幅所限,我们这里只介绍一些常用的功能,包括页面布局、新建、编辑、查询、删除、排序、分组、统计等功能。

    示例代码下载地址>>>>>

    页面总体布局

    首先,再来熟悉下前一篇Ext.NET-布局篇中提到的布局技术。

    新建WebForm页面,在ASPX文件中加入如下代码:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
         Inherits="WebFormDemo.GridDemo.Default" %>
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>Grid示例</title>
        <link rel="stylesheet" type="text/css" href="../Content/ExtjsExtra.css" />
    </head>
    <body>
        <ext:ResourceManager runat="server" />
        <ext:Viewport runat="server" Layout="FitLayout">
            <Items>
                <ext:GridPanel runat="server" ID="gridMain">
                    <DockedItems>
                        <ext:Toolbar runat="server" Dock="Top">
                            <Items>
                                <ext:Button runat="server" ID="btnAdd" Icon="Add">
                                </ext:Button>
                                <ext:ToolbarSeparator runat="server" />
                                <ext:Button runat="server" ID="btnDelete" Icon="Delete">
                                </ext:Button>
                            </Items>
                        </ext:Toolbar>
                        <ext:PagingToolbar runat="server" Dock="Bottom">
                        </ext:PagingToolbar>
                    </DockedItems>
                </ext:GridPanel>
            </Items>
        </ext:Viewport>
    </body>
    </html>
    

    以上代码在页面中加入了一个铺满整个浏览器窗口的GridPanel控件gridMain;顶端为工具栏,我们打算放一些动作按钮:如新建、删除等;底部未分页工具栏,用于GridPanel的分页;以上代码运行效果如下

    页面布局

    加入列

    上面的代码组织了页面的整体布局,而目前的GridPanel里还没有任何实质性的内容,接下来我们为GridPanel添加需要显示的列。
    </DockedItems></ext:GridPanel>之间加入如下代码

    <ColumnModel>
        <Columns>
            <ext:RowNumbererColumn runat="server" />
            <ext:Column runat="server" Text="姓名" />
            <ext:NumberColumn runat="server" Text="年龄" />
            <ext:BooleanColumn runat="server" Text="性别" TrueText="男" FalseText="女" />
            <ext:DateColumn runat="server" Text="生日" Format="yyyy-MM-dd" />
            <ext:Column runat="server" Text="民族" />
            <ext:Column runat="server" Text="籍贯" Flex="1" />
            <ext:Column runat="server" Text="备注" Flex="1" Hidden="true" />
        </Columns>
    </ColumnModel>
    

    以上代码为GridPanel定义了一个ColumnModel,并定义了ColumnModel所包含的列,需要说明的是

    • <ext:RowNumbererColumn runat="server" /> 行号,将为GridPanel自动生成行号;
    • <ext:Column runat="server" Text="籍贯" Flex="1" />中的Flex="1"指定了此列将占满剩余宽度,多个时自动计算各自宽度,与Ext.NET-布局篇中介绍的Flex一致。

    Grid列

    Ext.NET与ExtJS中的列类型对照

    Ext.NET不仅封装了ExtJS提供的列类型,并加入了一些自己的扩展,如下表

    Ext.NET类型 ExtJs类型 说明
    ActionColumn Ext.grid.column.Action  
    BooleanColumn Ext.grid.column.Boolean  
    CheckColumn Ext.grid.column.Check  
    Column Ext.grid.column.Column  
    CommandColumn - Ext.Net扩展
    ComponentColumn - Ext.Net扩展
    DateColumn Ext.grid.column.Date  
    HyperlinkColumn - Ext.Net扩展,3.x新加,参见此处
    ImageCommandColumn - Ext.Net扩展
    NumberColumn Ext.grid.column.Number  
    ProgressBarColumn - Ext.Net扩展
    RatingColumn - Ext.Net扩展
    RowNumbererColumn Ext.grid.column.RowNumberer  
    SummaryColumn - Ext.Net扩展,已过时
    TagColumn - Ext.Net扩展
    TemplateColumn Ext.grid.column.Template  
    TreeColumn Ext.tree.Column  
    WidgetColumn Ext.grid.column.Widget Ext.NET3.x新加,参见此处

    上表是Ext.NET与ExtJS列类型对照,每种类型的列提供了一些特殊功能,篇幅所限,不一一介绍,可参见链接指向的说明或示例。

    数据库及CRUD代码

    目前为止,页面布局完成了一大半,实际的工作中,我们的数据一般是存储在数据库中,所以,为了接下来的例子更贴近实际,先来创建数据库,本例中使用MS SQL Server 2012(Developer Edition)。

    数据库访问相关的技术不是本文的主要目的,为了使示例代码尽量简单,这里我们使用MS提供的SqlHelper类,详情参见Data Access Application Block for .NET下载地址.

    创建数据库

    创建名为ExtNetDemo的数据库加入测试数据,并执行如下脚本创建Person表:

    CREATE TABLE [dbo].[Person]
    (
    	[Id] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, 
        [Name] NVARCHAR(50) NULL, 
        [Age] INT NULL, 
        [Gender] BIT NULL, 
        [Birthdate] DATETIME NULL, 
        [Ethnic] NVARCHAR(50) NULL, 
        [Origo] NVARCHAR(200) NULL, 
        [Remarks] NVARCHAR(1000) NULL
    )
    INSERT INTO [dbo].[Person] ([Id], [Name], [Age], [Gender], [Birthdate], [Ethnic], [Origo], [Remarks]) 
    VALUES (N'f0d1d8bf-31f4-49c1-ac69-898138356b75', N'任盈盈', 18, 0, N'2015-08-08 00:00:00', N'汉族', N'陕西西安', N'备注')
    INSERT INTO [dbo].[Person] ([Id], [Name], [Age], [Gender], [Birthdate], [Ethnic], [Origo], [Remarks]) 
    VALUES (N'b1fef104-195d-422c-b729-f0c81149b6f0', N'令狐冲', 26, 1, N'2015-05-05 00:00:00', N'回族', N'河北沧州', N'备注说明')
    INSERT INTO [dbo].[Person] ([Id], [Name], [Age], [Gender], [Birthdate], [Ethnic], [Origo], [Remarks]) 
    VALUES (N'02ddad1a-91f0-4ec3-8d2b-3ed01c6fda6e', N'风清扬', 35, 1, N'1960-02-05 00:00:00', N'汉族', N'陕西渭南', N'大神')
    

    加入实体类

    我们继续使用Ext.NET-基础篇中使用的Person类,修改后的代码如下:

    using System;
    namespace WebFormDemo
    {
        public class Person
        {
            public Guid Id { get; set; }
            public string Name { get; set; }
            public int Age { get; set; }
            public bool Gender { get; set; }
            public DateTime Birthdate { get; set; }
            public string Ethnic { get; set; }
            public string Origo { get; set; }
            public string Remarks { get; set; }
            public string Desc { get; set; }
        }
    }
    

    加入CRUD代码

    CRUD代码很枯燥,也不是本文重点。建议用成熟的ORM库或代码生成工具来完成,这里为了使例子简单而尽量不引入更多的库。
    在ASPX.CS文件中加入如下代码

    #region DAL
    
    private readonly string m_connectionString = ConfigurationManager.ConnectionStrings["default"].ConnectionString;
    
    private IEnumerable<Person> GetPersonList(string where, string orderBy,
        IEnumerable<SqlParameter> paras, int pageIndex, int pageSize)
    {
        where = String.IsNullOrEmpty(where) ? "1=1" : where;
        orderBy = String.IsNullOrEmpty(orderBy) ? "Id desc" : orderBy;
        pageIndex = pageIndex > 0 ? pageIndex : 1;
        pageSize = pageSize > 0 ? pageSize : 20;
        int _rowStart = (pageIndex - 1) * pageSize + 1;
        int _rowEnd = pageIndex * pageSize;
        List<Person> result = new List<Person>();
        String sql =
            String.Format("SELECT * FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY {0} ) AS RowNumber,* FROM [" +
                "Person ] WHERE {1}) T WHERE T.RowNumber BETWEEN {2} AND {3} ",
            orderBy, where, _rowStart, _rowEnd);
        var ds = SqlHelper.ExecuteDataset(m_connectionString, System.Data.CommandType.Text, sql, paras.ToArray());
    
        if (ds != null && ds.Tables.Count > 0 &&
            ds.Tables[0].Columns.Count > 0 && ds.Tables[0].Rows.Count > 0)
        {
            foreach (DataRow row in ds.Tables[0].Rows)
            {
                Person p = new Person();
                p.Age = row.Field<int>("Age");
                p.Birthdate = row.Field<DateTime>("Birthdate");
                p.Ethnic = row.Field<string>("Ethnic");
                p.Gender = row.Field<bool>("Gender");
                p.Id = row.Field<Guid>("Id");
                p.Name = row.Field<string>("Name");
                p.Origo = row.Field<string>("Origo");
                p.Remarks = row.Field<string>("Remarks");
                result.Add(p);
            }
        }
    
        return result;
    }
    
    public int GetRecordCount(string where, IEnumerable<SqlParameter> paras)
    {
        int _result = 0;
        where = String.IsNullOrEmpty(where) ? "1=1" : where;
        string _sql = "SELECT COUNT(1) FROM [Person] ";
        if (!String.IsNullOrWhiteSpace(where))
        {
            _sql += " WHERE " + where;
        }
        Object _obj = SqlHelper.ExecuteScalar(m_connectionString, CommandType.Text, _sql, paras.ToArray());
        if (!Object.Equals(null, _obj) && !Object.Equals(DBNull.Value, _obj))
        {
            Int32.TryParse(_obj.ToString(), out _result);
        }
        return _result;
    }
    
    public bool Insert(Person p)
    {
        if (p == null)
        {
            return false;
        }
        p.Id = Guid.NewGuid();
        StringBuilder _sb = new StringBuilder();
        _sb.Append("INSERT INTO [Person] ");
        _sb.Append("([Id],[Name],[Age],[Gender],[Birthdate],[Ethnic],[Origo],[Remarks])");
        _sb.Append(" VALUES ");
        _sb.Append("(@Id,@Name,@Age,@Gender,@Birthdate,@Ethnic,@Origo,@Remarks)");
    
        SqlParameter[] _paras = {
                new SqlParameter("@Id", p.Id),
                new SqlParameter("@Name", p.Name),
                new SqlParameter("@Age", p.Age),
                new SqlParameter("@Gender", p.Gender),
                new SqlParameter("@Birthdate", p.Birthdate),
                new SqlParameter("@Ethnic", p.Ethnic),
                new SqlParameter("@Origo", p.Origo),
                new SqlParameter("@Remarks", p.Remarks)
                };
        int _rowsAffected = 0;
    
        _rowsAffected = SqlHelper.ExecuteNonQuery(m_connectionString,
            CommandType.Text, _sb.ToString(), _paras);
    
        return _rowsAffected > 0;
    }
    
    public bool Update(Person p)
    {
        if (p == null)
        {
            return false;
        }
    
        StringBuilder _sb = new StringBuilder();
        _sb.Append("UPDATE [Person] SET ");
        _sb.Append("[Id]=@Id,[Name]=@Name,[Age]=Age,[Gender]=@Gender,");
        _sb.Append("[Birthdate]=@Birthdate,[Ethnic]=@Ethnic,[Origo]=@Origo,[Remarks]=@Remarks ");
        _sb.Append(" WHERE [Id]=@Id");
    
        SqlParameter[] _paras = {
                new SqlParameter("@Id", p.Id),
                new SqlParameter("@Name", p.Name),
                new SqlParameter("@Age", p.Age),
                new SqlParameter("@Gender", p.Gender),
                new SqlParameter("@Birthdate", p.Birthdate),
                new SqlParameter("@Ethnic", p.Ethnic),
                new SqlParameter("@Origo", p.Origo),
                new SqlParameter("@Remarks", p.Remarks)
                };
        int _rowsAffected = 0;
    
        _rowsAffected = SqlHelper.ExecuteNonQuery(m_connectionString,
            CommandType.Text, _sb.ToString(), _paras);
    
        return _rowsAffected > 0;
    }
    
    public bool Delete(IEnumerable<Guid> ids)
    {
        if (ids == null || ids.Count() < 1)
        {
            return false;
        }
    
        string sql = "DELETE FROM [Person] WHERE ";
        string where = String.Empty;
        foreach (var key in ids)
        {
            if (String.IsNullOrEmpty(where))
            {
                where = String.Format(" [Id]='{0}' ", key);
            }
            else
            {
                where += String.Format(" OR [Id]='{0}' ", key);
            }
        }
        sql += where;
        int _rowsAffected = 0;
        _rowsAffected = SqlHelper.ExecuteNonQuery(m_connectionString, CommandType.Text, sql);
    
        return _rowsAffected > 0;
    }
    
    #endregion
    

    加入Store

    GridPanel必须与Store结合使用才能显示数据,这通常令Ext.NET新手感到不习惯,可以理解为Store为GridPanel提供了数据源,可参见ExtJs API中关于 GridPanel Store的说明

    Store通常也用在TreePanel、ComboBox中,当然,Store也可独立存在做其它使用。Store对多个实体对象进行封装,并在客户端缓存;Store通过Proxy加载数据,并提供了对实体对象集合的排序、查询等功能。

    关于Store的详细说明参见 Ext.data.Store

    为GridPanel加入Store可以使用两种方式:

    • GridPanel的StoreID属性指定一个外部定义的Store的ID;
    • 在GridPanel中定义,如<Store>此处定义真实的Store</Store>

    定义Store

    本例中我们使用后者,在<ext:GridPanel runat="server">...</ext:GridPanel>中加入如下代码:

    <Store>
        <ext:Store runat="server" ID="storeMain">
            <Model>
                <ext:Model runat="server" IDProperty="Id">
                    <Fields>
                        <ext:ModelField Name="Id" />
                        <ext:ModelField Name="Name" Type="String" />
                        <ext:ModelField Name="Age" Type="Int" />
                        <ext:ModelField Name="Gender" Type="Boolean" />
                        <ext:ModelField Name="Birthdate" Type="Date" />
                        <ext:ModelField Name="Ethnic" />
                        <ext:ModelField Name="Origo" />
                        <ext:ModelField Name="Remarks" />
                    </Fields>
                </ext:Model>
            </Model>
        </ext:Store>
    </Store>
    

    如上,Store中的Model定义了一个实体类型,与我们在服务器端的Person类相似,不过此处的Model存在于客户端浏览器中。

    • <ext:Model runat="server" IDProperty="Id">中的IDProperty="Id"指明了Model中的主键,默认值为"id"
    • <Fields></Fields>中定义了实体的字段;
    • 其中Type="Boolean"等指明了字段的数据类型,可选项有AutoBooleanDateFloatIntObjectString,默认为Auto

    详细说明请参考:

    1. Ext.data.Store
    2. Ext.data.Model
    3. Ext.data.field.Field

    为Column指定DataIndex

    上面我们为GridPanel定义好了Store,那么,如何使GridPanel中显示的列与Store.Model中定义的Field对应上呢?也就是说,如何为GridPanel指定每列要显示的内容呢?
    答案是为Columns中的每一列设置DataIndex属性,修改前面指定的<Columns></Columns>中的每列Column的DataIndex属性,如下

    <ColumnModel>
        <Columns>
            <ext:RowNumbererColumn runat="server" />
            <ext:Column runat="server" Text="姓名" DataIndex="Name" />
            <ext:NumberColumn runat="server" Text="年龄" DataIndex="Age"/>
            <ext:BooleanColumn runat="server" Text="性别" TrueText="男" 
                FalseText="女" DataIndex="Gender" />
            <ext:DateColumn runat="server" Text="生日" Format="yyyy-MM-dd" DataIndex="Birthdate"/>
            <ext:Column runat="server" Text="民族" DataIndex="Ethnic"/>
            <ext:Column runat="server" Text="籍贯" Flex="1" DataIndex="Origo"/>
            <ext:Column runat="server" Text="备注" Flex="1" Hidden="true" DataIndex="Remarks"/>
        </Columns>
    </ColumnModel>
    

    其中的每个DataIndex值,与上面定义的ModelField中的Name对应关系,注意大小写需要一致;

    为Store绑定服务器端数据库数据

    以上,服务器端CRUD代码已经就绪,GridPanel的显示已经就绪,接下来我们来将数据库中的数据显示到GridPanel中来。
    修改ASPX文件中Store的代码,如下

    <ext:Store runat="server" ID="storeMain" OnReadData="Main_ReadData">
        <Model>
            ...代码同上
        </Model>
        <Proxy>
            <ext:PageProxy />
        </Proxy>
    </ext:Store>
    

    为Store加入了OReadData服务器端事件,并加入了<ext:PageProxy />

    • OnReadData="Main_ReadData":这个没什么可说的,就是个传统的ASP.NET事件,签名有点特殊而已;
    • <Proxy><ext:PageProxy /></Proxy>:为Store指明了Proxy,Proxy负责为Store加载数据,其中PageProxy是Ext.NET从Ext.data.proxy.Server扩展,Ext.NET官方API文档中也没有更多说明,关于Proxy的说明,参见Ext.data.proxy.Proxy

    接着我们需要实现服务器端Main_ReadData事件(按条件过滤查询后面再介绍),服务器端ASPX.CS文件中加入如下代码

    protected void Main_ReadData(object sender, StoreReadDataEventArgs e)
    {
        Store store = sender as Store;
    
        string where = "1=1";
        string orderBy = "Id desc";
        e.Total = GetRecordCount(where, new List<SqlParameter>());
        store.DataSource = GetPersonList(where, orderBy,
            new List<SqlParameter>(), e.Page, e.Limit);
        store.DataBind();
    }
    

    这样,当浏览器中打开页面时自动调用了Main_ReadData方法,客户端的Store中也就自动加载了数据并使其显示在GridPanel中,效果如下:

    Store加载数据

    可以使用Sencha官方提供的Chrome扩展App Inspector for Sencha(墙外链接)查看Store中的数据,如下

    Sencha-Ext-Store

    此时Ext.NET生成的ExtJS代码如下(查看页面源代码可见):

    Ext.net.ResourceMgr.init({
        id: "ctl01",
        theme: "crisp",
        icons: ["Add", "Delete"]
    });
    Ext.onReady(function() {
        Ext.create("Ext.container.Viewport", {
            renderTo: Ext.getBody(),
            items: [{
                store: {
                    model: Ext.ClassManager.isCreated(Ext.id()) ? Ext.id() : Ext.define(Ext.id(), {
                        extend: "Ext.data.Model",
                        fields: [{
                            name: "Id"
                        },
                        {
                            name: "Name",
                            type: "string"
                        },
                        {
                            name: "Age",
                            type: "int"
                        },
                        {
                            name: "Gender",
                            type: "boolean"
                        },
                        {
                            name: "Birthdate",
                            type: "date",
                            dateFormat: "c"
                        },
                        {
                            name: "Ethnic"
                        },
                        {
                            name: "Origo"
                        },
                        {
                            name: "Remarks"
                        }],
                        idProperty: "Id"
                    }),
                    storeId: "storeMain",
                    autoLoad: true,
                    proxy: {
                        type: "page"
                    }
                },
                id: "gridMain",
                xtype: "grid",
                dockedItems: [{
                    dock: "top",
                    xtype: "toolbar",
                    items: [{
                        id: "btnAdd",
                        iconCls: "#Add"
                    },
                    {
                        xtype: "tbseparator"
                    },
                    {
                        id: "banAdd",
                        iconCls: "#Delete"
                    }]
                },
                {
                    dock: "bottom",
                    xtype: "pagingtoolbar",
                    displayInfo: true,
                    store: "storeMain"
                }],
                columns: {
                    items: [{
                        xtype: "rownumberer"
                    },
                    {
                        dataIndex: "Name",
                        text: "姓名"
                    },
                    {
                        xtype: "numbercolumn",
                        dataIndex: "Age",
                        text: "年龄"
                    },
                    {
                        xtype: "booleancolumn",
                        dataIndex: "Gender",
                        text: "性别",
                        falseText: "女",
                        trueText: "男"
                    },
                    {
                        xtype: "datecolumn",
                        dataIndex: "Birthdate",
                        text: "生日",
                        format: "Y-m-d"
                    },
                    {
                        dataIndex: "Ethnic",
                        text: "民族"
                    },
                    {
                        flex: 1,
                        dataIndex: "Origo",
                        text: "籍贯"
                    },
                    {
                        hidden: true,
                        flex: 1,
                        dataIndex: "Remarks",
                        text: "备注"
                    }]
                }
            }],
            layout: "fit"
        });
    });
    

    查询和排序

    上面我们使GridPanel显示数据库中的数据,但实际应用中我们一般都需要为客户提供按条件查询的功能,下面介绍Ext.NET/ExtJS中GridPanel的查询。。
    最常见的查询条件有:

    • 字符串:一般作为模糊查询;
    • 数字:区间查询;
    • 日期:区间查询;

    前面讲到Store提供了对实体对象集合的排序、查询等功能,因为Store存在于客户端,也就是Store提供了客户端的查询、排序功能,先来看看客户端的查询、排序功能。

    客户端查询&排序

    客户端的排序功能其实前面的已经实现了,由Store本身提供,定义好Store排序功能其实已经实现了。浏览器中单击GridPanel的列头就可以在正序和倒序之间切换。
    实现客户端的查询也很简单,修改GridPanel中的ColumnModel并加入GridFilters,代码如下

    <ext:GridPanel runat="server" ID="gridMain">
        ...略去部分代码
        <ColumnModel>
            <Columns>
                <ext:RowNumbererColumn runat="server" />
                <ext:Column runat="server" Text="姓名" DataIndex="Name">
                    <Filter>
                        <ext:StringFilter EmptyText="请输入" />
                    </Filter>
                </ext:Column>
                <ext:NumberColumn runat="server" Text="年龄" DataIndex="Age">
                    <Filter>
                        <ext:NumberFilter />
                    </Filter>
                </ext:NumberColumn>
                <ext:BooleanColumn runat="server" Text="性别" TrueText="男"
                    FalseText="女" DataIndex="Gender">
                    <Filter>
                        <ext:BooleanFilter YesText="男" NoText="女"/>
                    </Filter>
                </ext:BooleanColumn>
                <ext:DateColumn runat="server" Text="生日" Format="yyyy-MM-dd" DataIndex="Birthdate">
                    <Filter>
                        <ext:DateFilter BeforeText="截止" AfterText="起始" OnText="指定" />
                    </Filter>
                </ext:DateColumn>
                <ext:Column runat="server" Text="民族" DataIndex="Ethnic">
                    <Filter>
                        <ext:ListFilter Options="汉族,回族" />
                    </Filter>
                </ext:Column>
                <ext:Column runat="server" Text="籍贯" Flex="1" DataIndex="Origo">
                    <Filter>
                        <ext:StringFilter />
                    </Filter>
                </ext:Column>
                <ext:Column runat="server" Text="备注" Flex="1" Hidden="true" DataIndex="Remarks">
                    <Filter>
                        <ext:StringFilter />
                    </Filter>
                </ext:Column>
            </Columns>
        </ColumnModel>
        <Plugins>
            <ext:GridFilters runat="server" ID="filterMain" MenuFilterText="查询" />
        </Plugins>
    </ext:GridPanel>
    

    Ext.NET3.X定义方式与Ext.NET 2.X系列不一样,以前的定义方式参见Ext.NET2.5查询示例
    在列的右侧单击下拉箭头,输入或选择查询条件就可以进行客户端查询,如下图:

    GridPanel-Filter-Local

    如图查询生日为2015-05-10之前的数据,客户端查询出来的结果如上图。

    关于客户端的查询、排序就介绍到这里,更多可参见GridPanel with Local Filtering, Sorting and Paging

    服务器端查询&排序

    客户端的查询排序有其方便之处,但假如使用的是服务器端分页的话,客户端的查询/排序仅对当前页有效。
    接着,再来看看服务器端如何查询和排序,与客户端的查询排序大致相同,不过需要服务器端写一些代码,并在客户端的Store上加几个属性就可以。

    修改ASPX中的Store,代码如下

    <ext:Store runat="server" ID="storeMain" OnReadData="Main_ReadData"
            RemoteFilter="true" RemoteSort="true">
        ...略去部分代码
    </ext:Store>
    

    添加了RemoteFilter="true" RemoteSort="true"指明这个Store需要服务器端查询和排序。
    接着我们再来修改服务器端ASPX.CS文件中Main_ReadData方法代码,如下

    protected void Main_ReadData(object sender, StoreReadDataEventArgs e)
    {
        #region 查询条件
        StringBuilder sbFilter = new StringBuilder();
        List<SqlParameter> paras = new List<SqlParameter>();
                
        sbFilter.Append(" 1=1 ");
        string filtersStr = e.Parameters["filter"];
        if (!String.IsNullOrEmpty(filtersStr))
        {
            FilterConditions fs = new FilterConditions(filtersStr);
            foreach (FilterCondition fc in fs.Conditions)
            {
                string field = fc.Field;
                string comparison = fc.Comparison == Comparison.Eq ? "=" :
                    fc.Comparison == Comparison.Gt ? ">" :
                    fc.Comparison == Comparison.Lt ? "<" : "";
                switch (fc.Type)
                {
                    case FilterType.String:
                        sbFilter.AppendFormat(" AND {0} LIKE @{0} ", field);
                        paras.Add(new SqlParameter("@" + field,
                            "%" + fc.Value<string>() + "%"));
                        break;
                    case FilterType.Boolean:
                        sbFilter.AppendFormat("AND {0} = @{0}", field);
                        paras.Add(new SqlParameter("@" + field,
                            fc.Value<bool>()));
                        break;
                    case FilterType.Date:
                        DateTime theDate = fc.Value<DateTime>();
                        sbFilter.AppendFormat(" AND ([{0}] {1} '{2}') ",
                                    field, comparison, theDate);
                        break;
                    case FilterType.Number:
                        if (field == "Age")
                        {
                            int age = fc.Value<int>();
                            sbFilter.AppendFormat(" AND ([Age] {0} {1}) ",
                                    comparison, age);
                        }
                        break;
                    case FilterType.List:
                        string whereList = String.Empty;
                        for (int i = 0; i < fc.List.Count; i++)
                        {
                            whereList += String.Format(" {0} ([{1}] LIKE @{1}{2}) ",
                                i > 0 ? "OR" : "", field, i);
                            paras.Add(new SqlParameter(
                                String.Format("@{0}{1}", field, i),
                                String.Format("%{0}%", fc.List[i])
                                ));
                        }
                        if(!String.IsNullOrEmpty(whereList))
                        {
                            sbFilter.AppendFormat(" AND ({0}) ", whereList);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        #endregion
    
        #region 排序
        StringBuilder sbSort = new StringBuilder();
        foreach (DataSorter sort in e.Sort)
        {
            if (sbSort.Length > 0)
            {
                sbSort.Append(",");
            }
            sbSort.AppendFormat(" {0} {1} ", sort.Property,
                    sort.Direction == Ext.Net.SortDirection.ASC ? "ASC" : "DESC");
        }
        if (sbSort.Length <= 0)
        {
            sbSort.Append(" Id DESC ");
        }
        #endregion
    
        Store store = sender as Store;
        //总行数
        e.Total = GetRecordCount(sbFilter.ToString(), paras);
        //给Store绑定数据
        store.DataSource = GetPersonList(sbFilter.ToString(), sbSort.ToString(),
            paras, e.Page, e.Limit);
        store.DataBind();
    }
    

    如上第8行string filtersStr = e.Parameters["filter"]获取了客户端浏览器传递过来以JSON方式描述的的查询条件,这个参数怎么来的?是由上面提到Store中的Proxy来处理并提交给服务器端的,此处的<ext:PageProxy />是Ext.NET由Ext.data.proxy.Server扩展来的,此处的filter参数详见关于filterParam说明
    如上代码中用到的e.Sorte.Total也是由Proxy来处理的。

    现在运行如下图所示,查询条件为:年龄>18岁&&性别=男;
    Remote-Filter

    App Inspector for Sencha(墙外链接)查看Store中的数据,如下

    Remote-Filter-Inspector

    清空查询条件

    当GridPanel中的列较多时,当输入了一堆查询条件,要显示全部数据时需要逐一清除,为了使操作更方便,这时需要加入一个清空查询条件的功能。
    <ext:PagingToolbar runat="server" Dock="Bottom"></ext:PagingToolbar>之间加入如下代码,也就是在分页工具栏中放一个按钮,使用户可以一次清除所有的查询条件,代码如下

    <ext:PagingToolbar runat="server" Dock="Bottom">
        <Items>
            <ext:Button runat="server" ToolTip="清除所有查询条件"
                Icon="ApplicationViewList"
                Handler="#{filterMain}.clearFilters();">
            </ext:Button>
        </Items>
    </ext:PagingToolbar>
    

    clearFilters详细说明

    关于查询过滤就介绍到这里,篇幅有限,也就不一一介绍了,更多的请参见Ext.NET官方示例,提示,在官方示例左上方的查询框里输入filter可看到所有关于查询过滤的例子。

    分组统计&合计

    Ext.NET/ExtJS为GridPanel提供了丰富的插件,分组插件就是其中一个,使用起来也很简单。
    我们介绍下客户端分组及统计功能的使用。
    <ext:GridPanel runat="server" ID="gridMain"></ext:GridPanel>中加入如下代码

    <ext:GridPanel runat="server" ID="gridMain">
    ...略去部分代码
        <Features>
            <ext:GroupingSummary runat="server" HideGroupedHeader="false" StartCollapsed="false"
                GroupByText="以此分组" ShowGroupsText="分组显示"
                GroupHeaderTplString="{columnName}: {name} &nbsp;({rows.length}行)">
            </ext:GroupingSummary>
            <ext:Summary runat="server" Dock="Bottom" />
        </Features>
    </ext:GridPanel>
    

    其中GroupingSummary表示分组统计;Summary表示合计;
    在GridPanel的Columns中指定统计方式,修改Columns如下

    <ColumnModel>
        <Columns>
            <ext:RowNumbererColumn runat="server" />
            <ext:Column runat="server" Text="姓名" DataIndex="Name" Groupable="false">
                <Filter>
                    <ext:StringFilter EmptyText="请输入" />
                </Filter>
            </ext:Column>
            <ext:NumberColumn runat="server" Text="年龄" DataIndex="Age" Format="0" SummaryType="Average">
                <SummaryRenderer Handler="return Ext.Number.toFixed(value,2);" />
                <Filter>
                    <ext:NumberFilter />
                </Filter>
            </ext:NumberColumn>
            <ext:BooleanColumn runat="server" Text="性别" TrueText="男"
                FalseText="女" DataIndex="Gender" Groupable="false">
                <Filter>
                    <ext:BooleanFilter YesText="男" NoText="女" />
                </Filter>
            </ext:BooleanColumn>
            <ext:DateColumn runat="server" Text="生日" Format="yyyy-MM-dd" DataIndex="Birthdate" SummaryType="Min">
                <SummaryRenderer Handler="return Ext.util.Format.date(value,'Y-m-d');" />
                <Filter>
                    <ext:DateFilter BeforeText="截止" AfterText="起始" OnText="指定" />
                </Filter>
            </ext:DateColumn>
            <ext:Column runat="server" Text="民族" DataIndex="Ethnic">
                <Filter>
                    <ext:ListFilter Options="汉族,回族" />
                </Filter>
            </ext:Column>
            <ext:Column runat="server" Text="籍贯" Flex="1" DataIndex="Origo" Groupable="false">
                <Filter>
                    <ext:StringFilter />
                </Filter>
            </ext:Column>
            <ext:Column runat="server" Text="备注" Flex="1" Hidden="true" DataIndex="Remarks" Groupable="false">
                <Filter>
                    <ext:StringFilter />
                </Filter>
            </ext:Column>
        </Columns>
    </ColumnModel>
    
    1. Groupable="false"指明是否可以用当前列分组;
    2. SummaryType="Average"指明了统计方式,默认提供的统计方式有Average(平均值)、Count(数量)、Max(最大值)、Min(最小值)、None(不统计)、Sum(求和)。
    3. <SummaryRenderer Handler="return Ext.Number.toFixed(value,2);" />自定义了统计时的呈现方式,此处调用了Ext.Number.toFixed方法个使只显示2位小数。

    下图展示了以民族分组的效果,在民族列右侧单击下拉箭头,下拉菜单中单击以此分组,效果如下:

    GridPanel-GroupingSummary

    若需要页面加载时就以民族列分组显示,只需要在Store上加入GroupField属性即可,

    <ext:Store runat="server" ID="storeMain" OnReadData="Main_ReadData"
        RemoteFilter="true" RemoteSort="true" GroupField="Ethnic">
        ...略去部分代码
    </ext:Store>
    

    GroupField="Ethnic"指明了默认以Ethnic字段分组。

    以上介绍了客户端的分组统计&合计功能,关于服务器端的分组统计&合计与这个类似,具体可参见官方示例Grouping with Remote SummaryGrouping TotalRow,第二个例子中javascript方法updateTotal中,使用Ext.Net-基础篇中介绍的DirectMethod就可以实现服务器端的合计功能,这里不再赘述。

    新建&编辑&From布局

    接下来,再来看看如何实现新建和编辑。

    Form布局

    在ASPX文件中<body></body>中加入如下Window代码,

    <ext:Window runat="server" ID="winMain" Icon="Add" Hidden="true"
        Collapsible="true" Constrain="true" MinHeight="400" MinWidth="500" Modal="true"
        CloseAction="Hide" BodyPaddingSummary="10" Layout="FitLayout">
        <Buttons>
            <ext:Button runat="server" ID="btnMainSave" Text="保存">
                <DirectEvents>
                    <Click OnEvent="btnMainSave_Click"
                        Success="btnMainSave_Success"
                        Failure="btnMainSave_Failure">
                        <EventMask ShowMask="true" Target="Page" Msg="保存中..." />
                        <ExtraParams>
                            <ext:Parameter Name="model" Mode="Raw"
                                Value="#{frmMain}.getForm().getFieldValues()" />
                        </ExtraParams>
                    </Click>
                </DirectEvents>
            </ext:Button>
            <ext:Button runat="server" ID="btnMainCancel" Text="取消">
                <Listeners>
                    <Click Handler="#{winMain}.close();" />
                </Listeners>
            </ext:Button>
        </Buttons>
        <Items>
            <ext:FormPanel runat="server" ID="frmMain" Layout="VBoxLayout">
                <FieldDefaults LabelAlign="Left" LabelWidth="30" MsgTarget="Side" />
                <Plugins>
                    <ext:DataTip runat="server" />
                </Plugins>
                <LayoutConfig>
                    <ext:VBoxLayoutConfig Align="Stretch" />
                </LayoutConfig>
                <Items>
                    <ext:FieldContainer runat="server" Layout="HBoxLayout">
                        <Items>
                            <ext:TextField runat="server" FieldLabel="姓名" Name="Name"
                                AllowBlank="false" MaxLength="50" Flex="1" MarginSpec="0 10 0 0" />
                            <ext:NumberField runat="server" FieldLabel="年龄" Name="Age" Number="1" Flex="1"
                                MinValue="1" MaxValue="200" AllowDecimals="false" />
                        </Items>
                    </ext:FieldContainer>
                    <ext:FieldContainer runat="server" Layout="HBoxLayout">
                        <Items>
                            <ext:ComboBox runat="server" Name="Gender" FieldLabel="性别"
                                Editable="false" Width="100" MarginSpec="0 10 0 0">
                                <Items>
                                    <ext:ListItem Text="男" Value="true" />
                                    <ext:ListItem Text="女" Value="false" />
                                </Items>
                                <SelectedItems>
                                    <ext:ListItem Text="男" Value="true" />
                                </SelectedItems>
                            </ext:ComboBox>
                            <ext:ComboBox runat="server" Name="Ethnic" FieldLabel="民族"
                                Editable="false" Flex="1" MarginSpec="0 10 0 0">
                                <Items>
                                    <ext:ListItem Text="汉族" Value="汉族" />
                                    <ext:ListItem Text="回族" Value="回族" />
                                    <ext:ListItem Text="维吾尔族" Value="维吾尔族" />
                                </Items>
                                <SelectedItems>
                                    <ext:ListItem Text="汉族" Value="汉族" />
                                </SelectedItems>
                            </ext:ComboBox>
                            <ext:DateField runat="server" Name="Birthdate" FieldLabel="生日"
                                Format="yyyy-MM-dd" Editable="false" SelectedDate="1900-01-01" Flex="1" />
                        </Items>
                    </ext:FieldContainer>
                    <ext:TextField runat="server" Name="Origo" FieldLabel="籍贯" MaxLength="100" />
                    <ext:TextArea runat="server" Name="Remarks" FieldLabel="备注"
                        MaxLength="500" Flex="1" />
                    <ext:Hidden runat="server" Name="Id"
                        Text="00000000-0000-0000-0000-000000000000" />
                </Items>
            </ext:FormPanel>
        </Items>
    </ext:Window>
    
    • Hidden="true"表示这个Window是隐藏的,调试布局时可以设置为false,关于Window属性更多的说明请参见上一篇Ext.NET-布局篇中关于Window的介绍;
    • 使用FormPanel铺满整个Window区域,FormPanel的子控件使用VBoxLayout纵向布局,每个FieldContainer内部采用HBoxLayout横向布局,关于FieldContainer的示例可参见此处
    • FormPanel中<FieldDefaults LabelAlign="Left" LabelWidth="30" MsgTarget="Side" />分别配置了Label居左,宽度30px、错误消息显示在右边,可参见MsgTarget说明
    • <ext:VBoxLayoutConfig Align="Stretch" />以前介绍过,横向拉伸;
    • 隐藏域<ext:Hidden runat="server" Name="Id" />是为新建/编辑时使用的;
    • 我们为每个控件指定了Name属性而没有指明ID,这也是为新建和编辑时使用的,下面会介绍,可参考Name属性说明

    一个纵横交错的From布局就完成了,效果如下

    FormPanel-Layout

    新建

    上面完成了Form的布局,接下来我们完成新建的实际动作;
    1.首先修改winMainHidden属性值为true,因为我们不希望页面刚打开就蹦出来一个编辑窗口;接着修改GridPanel上方工具栏中的新建按钮代码如下:

    <ext:Button runat="server" ID="btnAdd" Icon="Add" ToolTip="新建">
        <Listeners>
            <Click Fn="btnAdd_Click" />
        </Listeners>
    </ext:Button>
    

    2.新建default.js文件;
    default.js

    var btnAdd_Click = function (sender, e) {
        //设置标题
        App.winMain.setTitle('新增');
        //App.winMain.setIconCls('fa fa-plus-circle');
        //显示Window
        App.winMain.show(sender);
    };
    
    /*
     * 服务器端执行成功时
     */
    var btnMainSave_Success = function (response, result,
            control, type, action, extraParams) {
        var form = App.frmMain;
        //还原From中内容
        form.getForm().reset();
        //关闭Window
        App.winMain.close();
        //刷新Grid数据
        App.gridMain.getStore().reload();
        //提示成功
        Ext.net.Notification.show({
            html: '保存成功',
            title: '提示'
        });
    };
    /*
     * 服务器端执行失败时
     */
    var btnMainSave_Failure = function (response, result,
            control, type, action, extraParams) {
        var msg = result.errorMessage;
        //提示失败
        Ext.net.Notification.show({
            html: msg,
            title: '提示'
        });
    };
    

    3.在ASPX文件<head></head>中加入引用

    <script type="text/javascript" src="default.js"></script>
    

    4.以上,前端的新建界面布局完成了,接下来我们来完成实际保存的后端代码,修改Window中的btnMainSave代码如下

    <ext:Button runat="server" ID="btnMainSave" Text="保存">
        <DirectEvents>
            <Click OnEvent="btnMainSave_Click"
                    Success="btnMainSave_Success"
                    Failure="btnMainSave_Failure">
                <EventMask ShowMask="true" Target="Page" Msg="保存中..." />
                <ExtraParams>
                    <ext:Parameter Name="model" Mode="Raw"
                            value="#{frmMain}.getForm().getFieldValues()"/>
                </ExtraParams>
            </Click>
        </DirectEvents>
    </ext:Button>
    
    • OnEvent="btnMainSave_Click"表示服务器端DirectEvent事件;
    • Success="btnMainSave_Success"服务器端DirectEvent执行成功时在客户端执行的javascript方法,在上面的default.js已定义;
    • Failure="btnMainSave_Failure"服务器端DirectEvent执行失败时在客户端执行的javascript方法,在上面的default.js已定义;
    • <ExtraParams></ExtraParams>中定义了此DirectEvent提交给服务器端的参数,此处用<ext:Parameter Name="model" Mode="Raw" value="#{frmMain}.getForm().getFieldValues()"/>定义了一个名称为model的参数,其中Mode="Raw"表示运算后的值,value="#{frmMain}.getForm().getFieldValues()"表示获取frmMain的所有Field中的值,前提是为每个Field设置了Name属性,如我们在年龄Field中定义的Name="Age",关于getFieldValues方法的详细说明可参见Ext.form.Basic.getFieldValues

    5.接着再来完成保存的服务器端DirectEvent事件,在aspx.cs文件中加入如下代码

    protected void btnMainSave_Click(object sender, DirectEventArgs e)
    {
        //客户端传过来的model参数
        string personJson = e.ExtraParams["model"];
        //反序列化为Person对象
        var model = JSON.Deserialize<Person>(personJson);
        //服务器端验证示例
        if (String.IsNullOrEmpty(model.Name))
        {
            e.ErrorMessage = "姓名不能为空";
            e.Success = false;
            return;
        }
        try
        {
            //若Id为空则新建,否则为更新
            e.Success = Guid.Empty == model.Id ? Insert(model) : Update(model);
        }
        catch(Exception ex)
        {
            //logs...ex
            e.ErrorMessage = "保存失败" + ex.Message;
            e.Success = false;
        }
    }
    

    以上,我们新建功能就完成了,当点击GridPanel上方的新增按钮将弹出winMain,填写完成后单击保存按钮,调用服务器端的btnMainSave_Click方法将数据存储到数据库中,关闭window,并提示保存成功。
    接下来再看看编辑功能。

    编辑

    由于上面的代码已经为编辑功能做好了铺垫,所以这里的编辑功能实现起来相对要简单的多。

    1.在GridPanel中每行的放置一个编辑按钮,

    <ext:GridPanel runat="server" ID="gridMain">
    	...略去部分代码
    	<ColumnModel>
    		<Columns>
    			<ext:RowNumbererColumn runat="server" />
    			<ext:CommandColumn runat="server">
    				<Commands>
    					<ext:GridCommand Icon="NoteEdit" CommandName="edit">
    						<ToolTip Text="编辑" />
    					</ext:GridCommand>
    				</Commands>
    				<Listeners>
    					<Command Fn="gridMain_Command" />
    				</Listeners>
    			</ext:CommandColumn>
    			...略去部分代码
    		</Columns>
    	</ColumnModel>
    	...略去部分代码
    </ext:GridPanel>
    

    2.点击编辑按钮时为frmMain加载数据并显示winMain,在default.js中加入gridMain_Command方法

    var gridMain_Command = function (item, command,
            record, recordIndex, cellIndex) {
        switch (command) {
            case 'edit':
                var form = App.frmMain;
                form.getForm().reset();
                form.loadRecord(record);
                App.winMain.show(item);
                break;
            default:
                break;
        }
    };
    

    其中form.loadRecord(record);frmMain从Store缓存的数据中加载数据到Form界面上,当然,这也得益于为每个Field定义了Name属性,关于loadRecord的说明参见Ext.form.Basic.loadRecord
    如此,编辑功能就完成了,当点击每行的编辑按钮时,将弹出编辑Window,如下图所示

    gridPanel-edit1

    gridPanel-edit2

    删除

    最开始时,我们已经定义好了删除按钮,现在是时候发挥它的作用了。
    这里我们用最常见的多选删除。

    1.为GridPanel添加CheckBox。

    <ext:GridPanel runat="server" ID="gridMain">
    	...略去部分代码
    		<SelectionModel>
    		<ext:CheckboxSelectionModel runat="server">
    			<Listeners>
    				<SelectionChange 
    					Handler="if(selected.length>0){#{btnDelete}.enable();}else{btnDelete.disable();}" />
    			</Listeners>
    		</ext:CheckboxSelectionModel>
    	</SelectionModel>
    </ext:GridPanel>
    

    这里用客户端Listener事件SelectionChange使选择行后btnDelete按钮可用。

    2.修改删除按钮

    <ext:Button runat="server" ID="btnDelete" Icon="Delete" Disabled="true">
        <DirectEvents>
            <Click OnEvent="btnDelete_Click"
                Success="#{gridMain}.getStore().reload();"
                Failure="Ext.net.Notification.show({html: result.errorMessage,title: '提示'});">
                <Confirmation ConfirmRequest="true"
                    Title="请确认" Message="删除后不可恢复,确定删除?" />
                <EventMask ShowMask="true" Target="Page" />
                <ExtraParams>
                    <ext:Parameter Name="models" Mode="Raw"
                        Value="Ext.encode(#{gridMain}.getRowsValues({selectedOnly:true}))" />
                </ExtraParams>
            </Click>
        </DirectEvents>
    </ext:Button>
    

    关于models参数的说明:
    getRowsValues方法是Ext.NET对GridPanel的扩展方法,此处的作用为获取选择的行数据,下面是此方法参数的说明

    // config :
    // - selectedOnly
    // - visibleOnly
    // - dirtyCellsOnly
    // - dirtyRowsOnly
    // - currentPageOnly
    // - excludeId
    // - filterRecord - function (record) - return false to exclude the record
    // - filterField - function (record, fieldName, value) - return false to exclude the field for particular record
    // -ignoreSubmitEmptyValue - true to ignore the ModelFields' SubmitEmptyValue option; defaults to false
    getRowsValues : function getRowsValues (config) {//...略}
    

    3.添加服务器端DirectEvent
    ASPX.CS文件中添加btnDelete_Click方法,代码如下

    protected void btnDelete_Click(object sender,DirectEventArgs e)
    {
    	string json = e.ExtraParams["models"];
    	var personList = JSON.Deserialize<List<Person>>(json);
    	var ids = from p in personList
    			  select p.Id;
    	try
    	{
    		Delete(ids.ToList());
    		e.Success = true;
    	}
    	catch (Exception ex)
    	{
    		//logs...
    		e.ErrorMessage = "删除失败." + ex.Message;
    		e.Success = false;
    	}
    }
    

    如上,删除功能也完成了。

    总结

    如上,我们介绍了一个完整的GridPanel使用实例,完整的代码如下
    ASPX

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
        Inherits="WebFormDemo.GridDemo.Default" %>
    
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Grid示例</title>
        <link rel="stylesheet" type="text/css" href="../Content/ExtjsExtra.css" />
        <script type="text/javascript" src="default.js"></script>
    </head>
    <body>
        <ext:ResourceManager runat="server" />
        <ext:Viewport runat="server" Layout="FitLayout">
            <Items>
                <ext:GridPanel runat="server" ID="gridMain">
                    <Store>
                        <ext:Store runat="server" ID="storeMain" OnReadData="Main_ReadData"
                            RemoteFilter="true" RemoteSort="true" GroupField="Ethnic">
                            <Model>
                                <ext:Model runat="server" IDProperty="Id">
                                    <Fields>
                                        <ext:ModelField Name="Id" />
                                        <ext:ModelField Name="Name" Type="String" />
                                        <ext:ModelField Name="Age" Type="Int" />
                                        <ext:ModelField Name="Gender" Type="Boolean" />
                                        <ext:ModelField Name="Birthdate" Type="Date" />
                                        <ext:ModelField Name="Ethnic" />
                                        <ext:ModelField Name="Origo" />
                                        <ext:ModelField Name="Remarks" />
                                    </Fields>
                                </ext:Model>
                            </Model>
                            <Proxy>
                                <ext:PageProxy />
                            </Proxy>
                        </ext:Store>
                    </Store>
                    <DockedItems>
                        <ext:Toolbar runat="server" Dock="Top">
                            <Items>
                                <ext:Button runat="server" ID="btnAdd" Icon="Add">
                                    <Listeners>
                                        <Click Fn="btnAdd_Click" />
                                    </Listeners>
                                </ext:Button>
                                <ext:ToolbarSeparator runat="server" />
                                <ext:Button runat="server" ID="btnDelete" Icon="Delete" Disabled="true">
                                    <DirectEvents>
                                        <Click OnEvent="btnDelete_Click"
                                            Success="#{gridMain}.getStore().reload();"
                                            Failure="Ext.net.Notification.show({html: result.errorMessage,title: '提示'});">
                                            <Confirmation ConfirmRequest="true"
                                                Title="请确认" Message="删除后不可恢复,确定删除?" />
                                            <EventMask ShowMask="true" Target="Page" />
                                            <ExtraParams>
                                                <ext:Parameter Name="models" Mode="Raw"
                                                    Value="Ext.encode(#{gridMain}.getRowsValues({selectedOnly:true}))" />
                                            </ExtraParams>
                                        </Click>
                                    </DirectEvents>
                                </ext:Button>
                            </Items>
                        </ext:Toolbar>
                        <ext:PagingToolbar runat="server" Dock="Bottom">
                            <Items>
                                <ext:Button runat="server" ToolTip="清除所有查询条件"
                                    Icon="ApplicationViewList"
                                    Handler="#{filterMain}.clearFilters();">
                                </ext:Button>
                            </Items>
                        </ext:PagingToolbar>
                    </DockedItems>
                    <ColumnModel>
                        <Columns>
                            <ext:RowNumbererColumn runat="server" />
                            <ext:CommandColumn runat="server">
                                <Commands>
                                    <ext:GridCommand Icon="NoteEdit" CommandName="edit">
                                        <ToolTip Text="编辑" />
                                    </ext:GridCommand>
                                </Commands>
                                <Listeners>
                                    <Command Fn="gridMain_Command" />
                                </Listeners>
                            </ext:CommandColumn>
                            <ext:Column runat="server" Text="姓名" DataIndex="Name" Groupable="false">
                                <Filter>
                                    <ext:StringFilter EmptyText="请输入" />
                                </Filter>
                            </ext:Column>
                            <ext:NumberColumn runat="server" Text="年龄" DataIndex="Age" Format="0" SummaryType="Sum">
                                <SummaryRenderer Handler="return Ext.Number.toFixed(value,2);" />
                                <Filter>
                                    <ext:NumberFilter />
                                </Filter>
                            </ext:NumberColumn>
                            <ext:BooleanColumn runat="server" Text="性别" TrueText="男"
                                FalseText="女" DataIndex="Gender" Groupable="false">
                                <Filter>
                                    <ext:BooleanFilter YesText="男" NoText="女" />
                                </Filter>
                            </ext:BooleanColumn>
                            <ext:DateColumn runat="server" Text="生日" Format="yyyy-MM-dd" DataIndex="Birthdate" SummaryType="Min">
                                <SummaryRenderer Handler="return Ext.util.Format.date(value,'Y-m-d');" />
                                <Filter>
                                    <ext:DateFilter BeforeText="截止" AfterText="起始" OnText="指定" />
                                </Filter>
                            </ext:DateColumn>
                            <ext:Column runat="server" Text="民族" DataIndex="Ethnic">
                                <Filter>
                                    <ext:ListFilter Options="汉族,回族" />
                                </Filter>
                            </ext:Column>
                            <ext:Column runat="server" Text="籍贯" Flex="1" DataIndex="Origo" Groupable="false">
                                <Filter>
                                    <ext:StringFilter />
                                </Filter>
                            </ext:Column>
                            <ext:Column runat="server" Text="备注" Flex="1" Hidden="true" DataIndex="Remarks" Groupable="false">
                                <Filter>
                                    <ext:StringFilter />
                                </Filter>
                            </ext:Column>
                        </Columns>
                    </ColumnModel>
                    <Plugins>
                        <ext:GridFilters runat="server" ID="filterMain" MenuFilterText="查询" />
                    </Plugins>
                    <Features>
                        <ext:GroupingSummary runat="server" HideGroupedHeader="false" StartCollapsed="false"
                            GroupByText="以此分组" ShowGroupsText="分组显示"
                            GroupHeaderTplString="{columnName}: {name} &nbsp;({rows.length}行)">
                        </ext:GroupingSummary>
                        <ext:Summary runat="server" Dock="Bottom" />
                    </Features>
                    <SelectionModel>
                        <ext:CheckboxSelectionModel runat="server">
                            <Listeners>
                                <SelectionChange
                                    Handler="if(selected.length>0){#{btnDelete}.enable();}else{btnDelete.disable();}" />
                            </Listeners>
                        </ext:CheckboxSelectionModel>
                    </SelectionModel>
                </ext:GridPanel>
            </Items>
        </ext:Viewport>
    
        <ext:Window runat="server" ID="winMain" Icon="Add" Hidden="true"
            Collapsible="true" Constrain="true" MinHeight="400" MinWidth="500" Modal="true"
            CloseAction="Hide" BodyPaddingSummary="10" Layout="FitLayout">
            <Buttons>
                <ext:Button runat="server" ID="btnMainSave" Text="保存">
                    <DirectEvents>
                        <Click OnEvent="btnMainSave_Click"
                            Success="btnMainSave_Success"
                            Failure="btnMainSave_Failure">
                            <EventMask ShowMask="true" Target="Page" Msg="保存中..." />
                            <ExtraParams>
                                <ext:Parameter Name="model" Mode="Raw"
                                    Value="#{frmMain}.getForm().getFieldValues()" />
                            </ExtraParams>
                        </Click>
                    </DirectEvents>
                </ext:Button>
                <ext:Button runat="server" ID="btnMainCancel" Text="取消">
                    <Listeners>
                        <Click Handler="#{winMain}.close();" />
                    </Listeners>
                </ext:Button>
            </Buttons>
            <Items>
                <ext:FormPanel runat="server" ID="frmMain" Layout="VBoxLayout">
                    <FieldDefaults LabelAlign="Left" LabelWidth="30" MsgTarget="Side" />
                    <Plugins>
                        <ext:DataTip runat="server" />
                    </Plugins>
                    <LayoutConfig>
                        <ext:VBoxLayoutConfig Align="Stretch" />
                    </LayoutConfig>
                    <Items>
                        <ext:FieldContainer runat="server" Layout="HBoxLayout">
                            <Items>
                                <ext:TextField runat="server" FieldLabel="姓名" Name="Name"
                                    AllowBlank="false" MaxLength="50" Flex="1" MarginSpec="0 10 0 0" />
                                <ext:NumberField runat="server" FieldLabel="年龄" Name="Age" Number="1" Flex="1"
                                    MinValue="1" MaxValue="200" AllowDecimals="false" />
                            </Items>
                        </ext:FieldContainer>
                        <ext:FieldContainer runat="server" Layout="HBoxLayout">
                            <Items>
                                <ext:ComboBox runat="server" Name="Gender" FieldLabel="性别"
                                    Editable="false" Width="100" MarginSpec="0 10 0 0">
                                    <Items>
                                        <ext:ListItem Text="男" Value="true" />
                                        <ext:ListItem Text="女" Value="false" />
                                    </Items>
                                    <SelectedItems>
                                        <ext:ListItem Text="男" Value="true" />
                                    </SelectedItems>
                                </ext:ComboBox>
                                <ext:ComboBox runat="server" Name="Ethnic" FieldLabel="民族"
                                    Editable="false" Flex="1" MarginSpec="0 10 0 0">
                                    <Items>
                                        <ext:ListItem Text="汉族" Value="汉族" />
                                        <ext:ListItem Text="回族" Value="回族" />
                                        <ext:ListItem Text="维吾尔族" Value="维吾尔族" />
                                    </Items>
                                    <SelectedItems>
                                        <ext:ListItem Text="汉族" Value="汉族" />
                                    </SelectedItems>
                                </ext:ComboBox>
                                <ext:DateField runat="server" Name="Birthdate" FieldLabel="生日"
                                    Format="yyyy-MM-dd" Editable="false" SelectedDate="1900-01-01" Flex="1" />
                            </Items>
                        </ext:FieldContainer>
                        <ext:TextField runat="server" Name="Origo" FieldLabel="籍贯" MaxLength="100" />
                        <ext:TextArea runat="server" Name="Remarks" FieldLabel="备注"
                            MaxLength="500" Flex="1" />
                        <ext:Hidden runat="server" Name="Id"
                            Text="00000000-0000-0000-0000-000000000000" />
                    </Items>
                </ext:FormPanel>
            </Items>
        </ext:Window>
    </body>
    </html>
    

    javascript文件

    var btnAdd_Click = function (sender, e) {
        //设置标题
        App.winMain.setTitle('新增');
        //App.winMain.setIconCls('fa fa-plus-circle');
        //显示Window
        App.winMain.show(sender);
    };
    
    /*
     * 服务器端执行成功时
     */
    var btnMainSave_Success = function (response, result,
            control, type, action, extraParams) {
        var form = App.frmMain;
        //还原From中内容
        form.getForm().reset();
        //关闭Window
        App.winMain.close();
        //刷新Grid数据
        App.gridMain.getStore().reload();
        //提示成功
        Ext.net.Notification.show({
            html: '保存成功',
            title: '提示'
        });
    };
    /*
     * 服务器端执行失败时
     */
    var btnMainSave_Failure = function (response, result,
            control, type, action, extraParams) {
        var msg = result.errorMessage;
        //提示失败
        Ext.net.Notification.show({
            html: msg,
            title: '提示'
        });
    };
    
    var gridMain_Command = function (item, command,
            record, recordIndex, cellIndex) {
        switch (command) {
            case 'edit':
                var form = App.frmMain;
                form.getForm().reset();
                form.loadRecord(record);
                App.winMain.show(item);
                break;
            default:
                break;
        }
    };
    

    ASPX.CS

    using Ext.Net;
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Text;
    
    namespace WebFormDemo.GridDemo
    {
        public partial class Default : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
            }
    
            protected void Main_ReadData(object sender, StoreReadDataEventArgs e)
            {
                #region 查询条件
                StringBuilder sbFilter = new StringBuilder();
                List<SqlParameter> paras = new List<SqlParameter>();
    
                sbFilter.Append(" 1=1 ");
                string filtersStr = e.Parameters["filter"];
                if (!String.IsNullOrEmpty(filtersStr))
                {
                    FilterConditions fs = new FilterConditions(filtersStr);
                    foreach (FilterCondition fc in fs.Conditions)
                    {
                        string field = fc.Field;
                        string comparison = fc.Comparison == Comparison.Eq ? "=" :
                            fc.Comparison == Comparison.Gt ? ">" :
                            fc.Comparison == Comparison.Lt ? "<" : "";
                        switch (fc.Type)
                        {
                            case FilterType.String:
                                sbFilter.AppendFormat(" AND {0} LIKE @{0} ", field);
                                paras.Add(new SqlParameter("@" + field,
                                    "%" + fc.Value<string>() + "%"));
                                break;
                            case FilterType.Boolean:
                                sbFilter.AppendFormat("AND {0} = @{0}", field);
                                paras.Add(new SqlParameter("@" + field,
                                    fc.Value<bool>()));
                                break;
                            case FilterType.Date:
                                DateTime theDate = fc.Value<DateTime>();
                                sbFilter.AppendFormat(" AND ([{0}] {1} '{2}') ",
                                            field, comparison, theDate);
                                break;
                            case FilterType.Number:
                                if (field == "Age")
                                {
                                    int age = fc.Value<int>();
                                    sbFilter.AppendFormat(" AND ([Age] {0} {1}) ",
                                            comparison, age);
                                }
                                break;
                            case FilterType.List:
                                string whereList = String.Empty;
                                for (int i = 0; i < fc.List.Count; i++)
                                {
                                    whereList += String.Format(" {0} ([{1}] LIKE @{1}{2}) ",
                                        i > 0 ? "OR" : "", field, i);
                                    paras.Add(new SqlParameter(
                                        String.Format("@{0}{1}", field, i),
                                        String.Format("%{0}%", fc.List[i])
                                        ));
                                }
                                if (!String.IsNullOrEmpty(whereList))
                                {
                                    sbFilter.AppendFormat(" AND ({0}) ", whereList);
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
                #endregion
    
                #region 排序
                StringBuilder sbSort = new StringBuilder();
                foreach (DataSorter sort in e.Sort)
                {
                    if (sbSort.Length > 0)
                    {
                        sbSort.Append(",");
                    }
                    sbSort.AppendFormat(" {0} {1} ", sort.Property,
                            sort.Direction == Ext.Net.SortDirection.ASC ? "ASC" : "DESC");
                }
                if (sbSort.Length <= 0)
                {
                    sbSort.Append(" Id DESC ");
                }
                #endregion
    
                Store store = sender as Store;
                //总行数
                e.Total = GetRecordCount(sbFilter.ToString(), paras);
                //给Store绑定数据
                store.DataSource = GetPersonList(sbFilter.ToString(), sbSort.ToString(),
                    paras, e.Page, e.Limit);
                store.DataBind();
            }
    
    
            protected void btnMainSave_Click(object sender, DirectEventArgs e)
            {
                //客户端传过来的model参数
                string personJson = e.ExtraParams["model"];
                //反序列化为Person对象
                var model = JSON.Deserialize<Person>(personJson);
                //服务器端验证示例
                if (String.IsNullOrEmpty(model.Name))
                {
                    e.ErrorMessage = "姓名不能为空";
                    e.Success = false;
                    return;
                }
                try
                {
                    //若Id为空则新建,否则为更新
                    e.Success = Guid.Empty == model.Id ? Insert(model) : Update(model);
                }
                catch (Exception ex)
                {
                    //logs...ex
                    e.ErrorMessage = "保存失败" + ex.Message;
                    e.Success = false;
                }
            }
    
            protected void btnDelete_Click(object sender, DirectEventArgs e)
            {
                string json = e.ExtraParams["models"];
                var personList = JSON.Deserialize<List<Person>>(json);
                var ids = from p in personList
                          select p.Id;
                try
                {
                    Delete(ids.ToList());
                    e.Success = true;
                }
                catch (Exception ex)
                {
                    //logs...
                    e.ErrorMessage = "删除失败." + ex.Message;
                    e.Success = false;
                }
            }
    
            #region DAL
    
            private readonly string m_connectionString = ConfigurationManager.ConnectionStrings["default"].ConnectionString;
    
            /// <summary>
            /// 从数据库中获取符合条件的数据
            /// </summary>
            /// <param name="where">sql where ...</param>
            /// <param name="orderBy">sql order by ...</param>
            /// <param name="paras">paras</param>
            /// <param name="pageIndex">页码</param>
            /// <param name="pageSize">页容量</param>
            /// <returns></returns>
            private IEnumerable<Person> GetPersonList(string where, string orderBy,
                IEnumerable<SqlParameter> paras, int pageIndex, int pageSize)
            {
                where = String.IsNullOrEmpty(where) ? "1=1" : where;
                orderBy = String.IsNullOrEmpty(orderBy) ? "Id desc" : orderBy;
                pageIndex = pageIndex > 0 ? pageIndex : 1;
                pageSize = pageSize > 0 ? pageSize : 20;
                int _rowStart = (pageIndex - 1) * pageSize + 1;
                int _rowEnd = pageIndex * pageSize;
                List<Person> result = new List<Person>();
                String sql =
                    String.Format("SELECT * FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY {0} ) AS RowNumber,* FROM [" +
                        "Person ] WHERE {1}) T WHERE T.RowNumber BETWEEN {2} AND {3} ",
                    orderBy, where, _rowStart, _rowEnd);
                var ds = SqlHelper.ExecuteDataset(m_connectionString, System.Data.CommandType.Text, sql, paras.ToArray());
    
                if (ds != null && ds.Tables.Count > 0 &&
                    ds.Tables[0].Columns.Count > 0 && ds.Tables[0].Rows.Count > 0)
                {
                    foreach (DataRow row in ds.Tables[0].Rows)
                    {
                        Person p = new Person();
                        p.Age = row.Field<int>("Age");
                        p.Birthdate = row.Field<DateTime>("Birthdate");
                        p.Ethnic = row.Field<string>("Ethnic");
                        p.Gender = row.Field<bool>("Gender");
                        p.Id = row.Field<Guid>("Id");
                        p.Name = row.Field<string>("Name");
                        p.Origo = row.Field<string>("Origo");
                        p.Remarks = row.Field<string>("Remarks");
                        result.Add(p);
                    }
                }
    
                return result;
            }
    
            /// <summary>
            /// 获取符合条件的记录行数
            /// </summary>
            /// <param name="where">条件,不需要WHERE关键字</param>
            /// <returns>符合条件的记录条数</returns>
            public int GetRecordCount(string where, IEnumerable<SqlParameter> paras)
            {
                int _result = 0;
                where = String.IsNullOrEmpty(where) ? "1=1" : where;
                string _sql = "SELECT COUNT(1) FROM [Person] ";
                if (!String.IsNullOrWhiteSpace(where))
                {
                    _sql += " WHERE " + where;
                }
                Object _obj = SqlHelper.ExecuteScalar(m_connectionString, CommandType.Text, _sql, paras.ToArray());
                if (!Object.Equals(null, _obj) && !Object.Equals(DBNull.Value, _obj))
                {
                    Int32.TryParse(_obj.ToString(), out _result);
                }
                return _result;
            }
    
            public bool Insert(Person p)
            {
                if (p == null)
                {
                    return false;
                }
                p.Id = Guid.NewGuid();
                StringBuilder _sb = new StringBuilder();
                _sb.Append("INSERT INTO [Person] ");
                _sb.Append("([Id],[Name],[Age],[Gender],[Birthdate],[Ethnic],[Origo],[Remarks])");
                _sb.Append(" VALUES ");
                _sb.Append("(@Id,@Name,@Age,@Gender,@Birthdate,@Ethnic,@Origo,@Remarks)");
    
                SqlParameter[] _paras = {
                new SqlParameter("@Id", p.Id),
                new SqlParameter("@Name", p.Name),
                new SqlParameter("@Age", p.Age),
                new SqlParameter("@Gender", p.Gender),
                new SqlParameter("@Birthdate", p.Birthdate),
                new SqlParameter("@Ethnic", p.Ethnic),
                new SqlParameter("@Origo", p.Origo),
                new SqlParameter("@Remarks", p.Remarks)
                };
                int _rowsAffected = 0;
    
                _rowsAffected = SqlHelper.ExecuteNonQuery(m_connectionString,
                    CommandType.Text, _sb.ToString(), _paras);
    
                return _rowsAffected > 0;
            }
    
            public bool Update(Person p)
            {
                if (p == null)
                {
                    return false;
                }
    
                StringBuilder _sb = new StringBuilder();
                _sb.Append("UPDATE [Person] SET ");
                _sb.Append("[Id]=@Id,[Name]=@Name,[Age]=Age,[Gender]=@Gender,");
                _sb.Append("[Birthdate]=@Birthdate,[Ethnic]=@Ethnic,[Origo]=@Origo,[Remarks]=@Remarks ");
                _sb.Append(" WHERE [Id]=@Id");
    
                SqlParameter[] _paras = {
                new SqlParameter("@Id", p.Id),
                new SqlParameter("@Name", p.Name),
                new SqlParameter("@Age", p.Age),
                new SqlParameter("@Gender", p.Gender),
                new SqlParameter("@Birthdate", p.Birthdate),
                new SqlParameter("@Ethnic", p.Ethnic),
                new SqlParameter("@Origo", p.Origo),
                new SqlParameter("@Remarks", p.Remarks)
                };
                int _rowsAffected = 0;
    
                _rowsAffected = SqlHelper.ExecuteNonQuery(m_connectionString,
                    CommandType.Text, _sb.ToString(), _paras);
    
                return _rowsAffected > 0;
            }
    
            public bool Delete(IEnumerable<Guid> ids)
            {
                if (ids == null || ids.Count() < 1)
                {
                    return false;
                }
    
                string sql = "DELETE FROM [Person] WHERE ";
                string where = String.Empty;
                foreach (var key in ids)
                {
                    if (String.IsNullOrEmpty(where))
                    {
                        where = String.Format(" [Id]='{0}' ", key);
                    }
                    else
                    {
                        where += String.Format(" OR [Id]='{0}' ", key);
                    }
                }
                sql += where;
                int _rowsAffected = 0;
                _rowsAffected = SqlHelper.ExecuteNonQuery(m_connectionString, CommandType.Text, sql);
    
                return _rowsAffected > 0;
            }
    
            #endregion
        }
    }
    

    原本计划在本文中介绍下导出和打印的功能,看大家需要再说吧:)

    参考
    [1] EXT.NET官方DEMO
    [2] Sencha ExtJS API
    [3] EXT.NET 官方API

    • 转载请注明出处 © guog 2015
  • 相关阅读:
    BEM(Block–Element-Modifier)
    http://element.eleme.io/#/zh-CN/component/quickstart
    Commit message 的写法规范。本文介绍Angular 规范(
    好的commit应该长啥样 https://github.com/torvalds/linux/pull/17#issuecomment-5654674
    代码管理
    if you have content fetched asynchronously on pages where SEO is important, SSR might be necessary
    Martin Fowler’s Active Record design pattern.
    The Zen of Python
    Introspection in Python How to spy on your Python objects Guide to Python introspection
    Object-Oriented Metrics: LCOM 内聚性的度量
  • 原文地址:https://www.cnblogs.com/goga21cn/p/EXT-NET-GRID.html
Copyright © 2011-2022 走看看