• 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
  • 相关阅读:
    Codeforces-799C-Fountains(分类讨论+线段树)
    HDU-3486-Interviewe(二分+RMQ)
    小技巧---查doc文档的index.html怎么用的和chm一样
    chm文件右边部分查看不了
    最长公共临时文档7
    拓展欧几里得临时文档5
    关于myeclipse代码提示的一些问题
    mysql--乱码
    三分--Football Goal(面积最大)
    printf的一个常用技巧
  • 原文地址:https://www.cnblogs.com/goga21cn/p/EXT-NET-GRID.html
Copyright © 2020-2023  润新知