【发布时间】:2010-04-12 13:59:14
【问题描述】:
我目前正在为 WSS 3.0 开发一个高度可配置的数据库查看器 webpart,我们将需要几个自定义共享点站点。提前为大量文字感到抱歉,但我担心有必要回顾整个问题。
作为背景信息并尽可能好地描述我的问题,我将首先告诉你 webpart 应该做什么:
基本上,webpart 包含一个 UpdatePanel,其中包含一个 GridView 和一个 SqlDataSource。数据源使用的选择查询可以通过 webbrowseable 属性设置,也可以从另一个 webpart 的使用者方法接收。 现在我想向 web 部件添加一个过滤功能,所以我想在标题行中为应该可过滤的每一列提供一个下拉列表。由于选择查询是完全动态的,而且我在设计时不知道哪些列可以过滤,所以我决定添加一个 webbrowseable 属性来包含一个带有过滤信息的 xml 格式的字符串。
所以我在 gridview 的 OnRowCreated 中添加了以下内容:
void gridView_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
for (int i = 0; i < e.Row.Cells.Count; i++)
{
if (e.Row.Cells[i].GetType() == typeof(DataControlFieldHeaderCell))
{
string headerText = ((DataControlFieldHeaderCell)e.Row.Cells[i]).ContainingField.HeaderText;
// add sorting functionality
if (_allowSorting && !String.IsNullOrEmpty(headerText))
{
Label l = new Label();
l.Text = headerText;
l.ForeColor = Color.Blue;
l.Font.Bold = true;
l.ID = "Header" + i;
l.Attributes["title"] = "Sort by " + headerText;
l.Attributes["onmouseover"] = "this.style.cursor = 'pointer'; this.style.color = 'red'";
l.Attributes["onmouseout"] = "this.style.color = 'blue'";
l.Attributes["onclick"] = "__doPostBack('" + panel.UniqueID + "','SortBy$" + headerText + "');";
e.Row.Cells[i].Controls.Add(l);
}
// check if this column shall be filterable
if (!String.IsNullOrEmpty(filterXmlData))
{
XmlNode columnNode = GetColumnNode(headerText);
if (columnNode != null)
{
string dataValueField = columnNode.Attributes["DataValueField"] == null ? "" : columnNode.Attributes["DataValueField"].Value;
string filterQuery = columnNode.Attributes["FilterQuery"] == null ? "" : columnNode.Attributes["FilterQuery"].Value;
if (!String.IsNullOrEmpty(dataValueField) && !String.IsNullOrEmpty(filterQuery))
{
SqlDataSource ds = new SqlDataSource(_conStr, filterQuery);
DropDownList cbx = new DropDownList();
cbx.ID = "FilterCbx" + i;
cbx.Attributes["onchange"] = "__doPostBack('" + panel.UniqueID + "','SelectionChange$" + headerText + "$' + this.options[this.selectedIndex].value);";
cbx.Width = 150;
cbx.DataValueField = dataValueField;
cbx.DataSource = ds;
cbx.DataBound += new EventHandler(cbx_DataBound);
cbx.PreRender += new EventHandler(cbx_PreRender);
cbx.DataBind();
e.Row.Cells[i].Controls.Add(cbx);
}
}
}
}
}
}
}
GetColumnNode() 检查过滤器属性是否存在当前列的节点,其中包含有关 DropDownList 应绑定到的字段的信息,以及用于填充项目的查询。
在 cbx_PreRender() 中,我检查 ViewState 并选择一个项目以防回发。
在 cbx_DataBound() 中,我只是将工具提示添加到列表项,因为下拉列表具有固定宽度。
以前,我使用 DDL 的 AutoPostback 和 SelectedIndexChanged 来过滤网格,但令我失望的是,它并不总是被触发。现在我在 OnLoad 中检查 __EVENTTARGET 和 __EVENTARGUMENT 并在回发事件是由于 DDL 中的选择更改引起时调用一个函数:
private void FilterSelectionChanged(string columnName, string selectedValue)
{
columnName = "[" + columnName + "]";
if (selectedValue.IndexOf("--") < 0 ) // "-- All --" selected
{
if (filter.ContainsKey(columnName))
filter[columnName] = "='" + selectedValue + "'";
else
filter.Add(columnName, "='" + selectedValue + "'");
}
else
{
filter.Remove(columnName);
}
gridView.PageIndex = 0;
}
"filter" 是一个 HashTable,它存储在 ViewState 中用于持久化过滤器(在网络上的某个地方获得了这个示例,不记得在哪里)。
在 webpart 的 OnPreRender 中,我调用了一个函数,该函数读取 ViewState 并将 filterExpression 应用于数据源(如果有)。我想我必须把它放在这里,因为如果有另一个回发(例如用于排序),则不再应用过滤器。
private void ApplyGridFilter()
{
string args = " ";
int i = 0;
foreach (object key in filter.Keys)
{
if (i == 0)
args = key.ToString() + filter[key].ToString();
else
args += " AND " + key.ToString() + filter[key].ToString();
i++;
}
dataSource.FilterExpression = args;
ViewState.Add("FilterArgs", filter);
}
protected override void OnPreRender(EventArgs e)
{
EnsureChildControls();
if (WebPartManager.DisplayMode.Name == "Edit")
{
errMsg = "Webpart in Edit mode...";
return;
}
if (useWebPartConnection == true) // get select-query from consumer webpart
{
if (provider != null)
{
dataSource.SelectCommand = provider.strQuery;
}
}
try
{
int currentPageIndex = gridView.PageIndex;
if (!String.IsNullOrEmpty(m_SortExpression))
{
gridView.Sort("[" + m_SortExpression + "]", m_SortDirection);
}
gridView.PageIndex = currentPageIndex; // for some reason, the current pageindex resets after sorting
ApplyGridFilter();
gridView.DataBind();
}
catch (Exception ex)
{
Functions.ShowJavaScriptAlert(Page, ex.Message);
}
base.OnPreRender(e);
}
所以我设置了 filterExpression 和调用 DataBind()。我不知道这是否可以在这个后期阶段.. 毕竟没有很多 asp.net 经验。如果有人可以提出更好的解决方案,请给我提示。
到目前为止,这一切都很好,除非我有两个或更多过滤器并将它们设置为返回零记录的组合。 Bam ... gridview 完全消失了 - 没有将过滤器改回来的可能性。所以我用谷歌搜索发现我必须子类化gridview才能始终显示标题行。我找到了this solution,并通过一些修改实现了它。
标题行得到显示,即使返回的结果不包含任何行,我也可以更改过滤器。但最后是我目前的问题:
当我设置了两个或多个返回零行的过滤器,并且我将一个过滤器更改回应该返回行的东西时,gridview 保持为空(尽管呈现了寻呼机)。我必须完全刷新页面才能重置过滤器。调试时,我可以在网格的重写 CreateChildControls 中看到,基本方法确实返回 > 0,但无论如何...... gridView.RowCount 在数据绑定后仍然为 0。有人知道这里出了什么问题吗?
【问题讨论】:
标签: asp.net gridview drop-down-menu filter