В настоящее время я работаю над настраиваемой веб-частью Просмотр базы данных для WSS 3.0, которая нам понадобится для нескольких настраиваемых сайтов sharepoint. Заранее извините за большую стену текста, но я боюсь, что необходимо вспомнить весь вопрос.
В качестве справочной информации и для того, чтобы как можно лучше описать мою проблему, я начну с рассказа о том, что должна делать веб-часть:
В основном веб-часть содержит UpdatePanel, которая содержит GridView и SqlDataSource. Запрос на выборку, который использует источник данных, может быть задан через веб-обозреваемые свойства или получен из потребительского метода из другой веб-части.
Теперь я хотел добавить функцию фильтрации к веб-части, поэтому я хочу выпадающий список в заголовке для каждого столбца, который должен быть фильтруемым. Поскольку запрос на выборку является полностью динамическим, и я не знаю во время разработки, какие столбцы должны фильтроваться, я решил добавить свойство webbrowseable, чтобы оно содержало строку в формате XML с информацией фильтра.
Итак, я добавил следующее в OnRowCreated gridview:
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 () я просто добавляю подсказки к элементам списка, поскольку выпадающий список имеет фиксированную ширину.
Ранее я использовал AutoPostback и SelectedIndexChanged из DDL для фильтрации сетки, но, к моему разочарованию, это не всегда срабатывало. Теперь я проверяю __EVENTTARGET и __EVENTARGUMENT в OnLoad и вызываю функцию, когда событие обратной передачи было связано с изменением выбора в 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;
}
«фильтр» - это HashTable, который хранится во ViewState для сохранения фильтров (этот пример был где-то в сети, не помню где).
В 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. Если кто-то может предложить лучшее решение, пожалуйста, дайте мне подсказку.
Пока все это прекрасно работает, за исключением случаев, когда у меня есть два или более фильтров и я установил их в комбинацию, которая возвращает ноль записей. Бам ... gridview исчез, полностью - без возможности замены фильтров обратно. Так что я погуглил и обнаружил, что мне нужно создать подкласс gridview, чтобы всегда показывать заголовок. Я нашел это решение и реализовал его с некоторыми изменениями.
Отображается заголовок get, и я могу изменить фильтры, даже если возвращаемый результат не содержит строк. Но, наконец, к моей текущей проблеме:
Когда у меня установлено два или более фильтров, которые возвращают ноль строк, и я заменяю один фильтр на то, что должно возвращать строки, gridView остается пустым (хотя пейджер отображается). Я должен полностью обновить страницу, чтобы сбросить фильтры. При отладке я вижу в переопределенных CreateChildControls сетки, что базовый метод действительно возвращает> 0, но в любом случае ... gridView.RowCount остается 0 после привязки данных. У кого-нибудь есть идеи, что здесь происходит?