带搜索选项的 ASP.NET GridView (SearchableGridView)






4.83/5 (35投票s)
一个在页脚带有搜索选项的 ASP.NET GridView。

引言
我一直在寻找在 ASP.NET GridView 控件中包含搜索文本框选项的方法。 我找不到一个优雅的解决方案,并决定自己实现它。 所以这是我解决这个问题的方法。
为什么要使用此解决方案?
您可以使用此解决方案非常容易地在网格中实现行过滤。 只需处理引发的搜索事件即可执行搜索和过滤操作。 此外,此 GridView 还允许您设置选项以显示行序列号、总行数,并在 GridView 中没有可用行时显示标题和页脚。(默认情况下,当 GridView 没有要显示的行时,标题和页脚是隐藏的。)
解决方案
我所做的事情
- 我扩展了 GridView并创建了一个SearchableGridView类。
- 添加了一个 TemplateColumn来显示行号。
- 在页脚中添加了控件来处理搜索操作。
- 当触发搜索时,引发一个以搜索字符串作为参数的事件。
代码
我将 GridView 控件扩展为 SearchableGridView,以便在 GridView 的页脚中包含搜索选项。
public class SearchGridView : GridView
为了显示行序列号,我创建了以下模板列
public class NumberColumn : ITemplate
{
    public void InstantiateIn(Control container)
    {
    }
}
在 SearchableGridView 中,我重写了 OnInit 函数,以在 ShowRowNumber 标志打开时添加模板列作为第一列来显示行序列号。
protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    //If showrownumber option is turned on then add 
    //the template column as the first column.
    if (!IsDesign() && ShowRowNumber) 
    {
        TemplateField tmpCol = new TemplateField();
        NumberColumn numCol = new NumberColumn();
        tmpCol.ItemTemplate = numCol;
        // Insert this as the first column
        this.Columns.Insert(0, tmpCol);
    }
}
每次创建行时都会调用 OnRowCreated 方法。 在运行时,根据 RowType,我添加搜索控件和行号的标签(在页脚中)、行号(在每一行中)以及行号列的列标题(在标题中)。
protected override void OnRowCreated(GridViewRowEventArgs e)
{
    base.OnRowCreated(e);
    if (!IsDesign()) //During Runtime
    {
        if (e.Row.RowType == DataControlRowType.Footer)
        {
            //If ShowFooter is set to true
            if (ShowFooter && e.Row.Cells.Count > 0)
            {
                //If TotalRows has to be shown
                if (ShowTotalRows)
                {
                    e.Row.Cells[0].Text = ViewState[NO_OF_ROWS] + " Rows.";
                }
                if (e.Row.Cells[e.Row.Cells.Count - 1].Controls.Count == 0)
                {
                    //Create the search control
                    Table table = new Table();
                    table.Style.Add("width", "100%");
                    table.Style.Add("align", "right");
                    TableRow tr = new TableRow();
                    TableCell tc = new TableCell();
                    tc.Style.Add("align", "right");
                    tc.Style.Add("width", "100%");
                    //Populate the dropdownlist with the Ids
                    //of the columns to be filtered
                    if (_ddlFinder.Items.Count == 0)
                        SetFilter();
                    _btnSearch.Width = 20;
                    _btnSearch.Height = 20;
                    _btnSearch.ImageAlign = ImageAlign.AbsMiddle;
                    _btnSearch.AlternateText = "Search";
                    //Assign the function that is called when search button is clicked
                    _btnSearch.Click += new ImageClickEventHandler(_btnSearch_Click);
                    
                    tc.Controls.Add(_ddlFinder);
                    tc.Controls.Add(_tbSearch);
                    tc.Controls.Add(_btnSearch);
                    tr.Cells.Add(tc);
                    table.Rows.Add(tr);
                    _pnlSearchFooter.Controls.Add(table);
                    e.Row.Cells[e.Row.Cells.Count - 1].Controls.Add(_pnlSearchFooter);
                
                }
            }
        }
        if (e.Row.RowType == DataControlRowType.Header)
        {
            // If ShowHeader is set to true and 
            // If Row number has to be shown
            if (ShowRowNumber && ShowHeader) 
            {
                e.Row.Cells[0].Text = "Sno";
            }
        }
        else if (e.Row.RowType == DataControlRowType.DataRow)
        {
            if (ShowRowNumber)
            {
                //Set the row number in every row
                e.Row.Cells[0].Text = (e.Row.RowIndex + 
                  (this.PageSize * this.PageIndex) + 1).ToString();
            }
        }
    }
}
SearchableGridView 的 SearchFilters 属性设置搜索选项的下拉列表值。 列表项的 Text 属性对应于下拉列表中的显示名称,列表项的 Value 属性是数据源的列名。
public void SetFilter()
{
    _ddlFinder.Items.Clear();
    //Copy the items to the dropdownlist
    foreach (ListItem li in SearchFilters)
        _ddlFinder.Items.Add(li);
}
现在,让我们继续处理搜索事件。 为此,我创建了一个委托并在点击搜索按钮时触发 SearchGrid 事件。 搜索字符串使用语法 _ddlFinder.SelectedValue + " like '" + _tbSearch.Text.Trim() + "%'" 形成。
public delegate void SearchGridEventHandler(string _strSearch);
public event SearchGridEventHandler SearchGrid;
void _btnSearch_Click(object sender, ImageClickEventArgs e)
{
    string sSearchText = ConstructSearchString();
    OnSearchGrid(sSearchText);
}
protected string ConstructSearchString()
{
    string _strText = _tbSearch.Text.Trim();
    if (_strText == string.Empty)
        return string.Empty;
    return _ddlFinder.SelectedValue + " like '" + _strText + "%'";
}
protected void OnSearchGrid(string _strSearch)
{
    if (SearchGrid != null)
    {
        SearchGrid(_strSearch);
    }
}
当没有返回任何行时显示页脚
GridView 的默认属性是在没有绑定任何行时隐藏标题和页脚。 设置一个空的 item 模板只会显示模板,而不会显示标题或页脚。 在我们的例子中,无论绑定到 SearchableGridView 的行数是多少,都必须始终显示页脚,因为搜索选项应该是可见的。 为了实现这一点,我必须重写 CreateChildControls 方法,如下所示
protected override int CreateChildControls(System.Collections.IEnumerable dataSource, 
                                           bool dataBinding)
{
    int count = base.CreateChildControls(dataSource, dataBinding);
    //  no rows in grid. create header and footer in this case
    if (count == 0 && (ShowEmptyFooter || ShowEmptyHeader))
    {
        //  create the table
        Table table = this.CreateChildTable();
        DataControlField[] fields;
        if (this.AutoGenerateColumns)
        {
            PagedDataSource source = new PagedDataSource();
            source.DataSource = dataSource;
            System.Collections.ICollection autoGeneratedColumns = 
                                           this.CreateColumns(source, true);
            fields = new DataControlField[autoGeneratedColumns.Count];
            autoGeneratedColumns.CopyTo(fields, 0);
        }
        else
        {
            fields = new DataControlField[this.Columns.Count];
            this.Columns.CopyTo(fields, 0);
        }
        if (ShowEmptyHeader)
        {
            //  create a new header row
            GridViewRow headerRow = base.CreateRow(-1, -1, DataControlRowType.Header, 
                                                   DataControlRowState.Normal);
            this.InitializeRow(headerRow, fields);
            // Fire the OnRowCreated event to handle showing row numbers
            OnRowCreated(new GridViewRowEventArgs(headerRow));
            //  add the header row to the table
            table.Rows.Add(headerRow);
        }
        //  create the empty row
        GridViewRow emptyRow = new GridViewRow(-1, -1, DataControlRowType.EmptyDataRow, 
                                               DataControlRowState.Normal);
        TableCell cell = new TableCell();
        cell.ColumnSpan = fields.Length;
        cell.Width = Unit.Percentage(100);
        //  respect the precedence order if both EmptyDataTemplate
        //  and EmptyDataText are both supplied ...
        if (this.EmptyDataTemplate != null)
        {
            this.EmptyDataTemplate.InstantiateIn(cell);
        }
        else if (!string.IsNullOrEmpty(this.EmptyDataText))
        {
            cell.Controls.Add(new LiteralControl(EmptyDataText));
        }
        emptyRow.Cells.Add(cell);
        table.Rows.Add(emptyRow);
        if (ShowEmptyFooter)
        {
            //  create footer row
            GridViewRow footerRow = base.CreateRow(-1, -1, DataControlRowType.Footer, 
                                                   DataControlRowState.Normal);
            this.InitializeRow(footerRow, fields);
            // Fire the OnRowCreated event to handle showing
            // search tool and total number of rows
            OnRowCreated(new GridViewRowEventArgs(footerRow));
            //  add the footer to the table
            table.Rows.Add(footerRow);
        }
        this.Controls.Clear();
        this.Controls.Add(table);
    }
    return count;
}
工作示例
让我借助一个例子来说明上面的控件。 为此,我使用了 NorthWind 数据库中的 Customers 表。
步骤 1:创建一个带有 Select 查询的数据源 dsCustomers:"SELECT CustomerID, CompanyName, Address, City, Country FROM Customers"。
步骤 2:创建一个 SearchableGridView 实例并自定义 SearchFilters 属性,以拥有可以在其上执行搜索的列的列表。

步骤 3:添加两个隐藏字段 hfSearchText 和 hfSort,以分别存储搜索文本和排序文本。
步骤 4:实现 SearchGrid 事件以在数据源上设置搜索字符串,并在 SearchableGridView 中过滤行。 hfSearchText 和 hfSort 是保存 SearchableGridView 的搜索字符串和排序字符串的隐藏字段。 BindData 方法在过滤和排序后绑定数据。
protected void SearchGridView1_SearchGrid(string _strSearch)
{
    hfSearchText.Value = _strSearch;
    BindData();
}
protected void SearchGridView1_Sorting(object sender, GridViewSortEventArgs e)
{
    //If hfSort has the same value as before, 
    //the sorting should be done in descending order
    if (hfSort.Value == e.SortExpression)
        hfSort.Value = e.SortExpression + " Desc";
    else
        hfSort.Value = e.SortExpression;
    BindData();
}
void BindData()
{
    //hfSearchText has the search string returned from the grid.
    if (hfSearchText.Value != "")
        dsCustomers.SelectCommand += " where " + hfSearchText.Value;
    DataView dv = (DataView)dsCustomers.Select(new DataSourceSelectArguments());
    //hfSort has the sort string returned from the grid.
    if (hfSort.Value != "")
        dv.Sort = hfSort.Value;
    SearchGridView1.DataSource = dv;
    try
    {
        SearchGridView1.DataBind();
    }
    catch (Exception exp)
    {
        //If databinding threw exception b’coz current 
        //page index is > than available page index
        SearchGridView1.PageIndex = 0;
        SearchGridView1.DataBind();
    }
    finally
    {
        //Select the first row returned
        if (SearchGridView1.Rows.Count > 0)
            SearchGridView1.SelectedIndex = 0;
    }
}
结论
当网格中有大量行时,SearchableGridView 将非常有用。 无需太多麻烦即可实现搜索行。


