带搜索选项的 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
将非常有用。 无需太多麻烦即可实现搜索行。