[SharePoint]SPGridView Control – Часть 2: Фильтрация

sharepoint2010-2013
Данная статья является продолжением статьи «[SharePoint]SPGridView Control – Часть 1: Что такое SPGridView и с чем его едят»

В предыдущей части сериала про SPGridView мы сформировали объект SPGridView, забили дата-сорс, а также описали разбивку на страницы. Данная статья целиком и полностью посвящена фильтрации. Попробуем добавить к нашему проекту большую толику интерактивности.
Фильтрация — это один из тех бенефитов, которые выгодно оттеняют использование SPGridView; тогда как в стандартом ASP:GridView приходилось по-изгаляться, чтобы организовать фильтрацию. Более того, с точки зрения конечного пользователя интерфейса, фильтрация — логичный и вполне ожидаемый элемент функционала, и если его нет — последнему становится грустно.
Нет фильтрации? Не надо так! 😉
В отличии от сортировки и разбивки на страницы, с фильтрацией придется усердно по-пыхтеть, однако в целом все не так страшно.

Первым-наперво потребуется активировать фильтрацию в нашем объекте:

// Filtering
grid.AllowFiltering = true;
grid.FilterDataFields = «,Name,Region,Total Sales»;
grid.FilteredDataSourcePropertyName = «FilterExpression»;
grid.FilteredDataSourcePropertyFormat = «{1} = ‘{0}'»;

Параметр FilterDataFields задает в объекте колонки, по которым мы хотим разрешить фильтрацию. В нашем случае, допустим, что фильтроваться будут все колонки, кроме идентификатора. Для колонок, по которым нам не требуется активировать фильтрацию мы оставляем пустое место (это важно). В коде выше это выражено запятой в самом начале.

Параметр FilteredDataSourcePropertyFormat содержит формат выражения для фильтрации. Чем-то напоминает SQL’ский. Конечная строка фильтрации будет сохраняться в свойство FilteredDataSourcePropertyName нашего ObjectDataSource

Мягко говоря, вышеописанного кода должно хватить, чтобы активировать колонку, по крайней мере, так заверяют несколько забугорных источников.
Однако допустим следующий сценарий: Мы фильтруем содержимое грида по какой-либо колонке, а затем хотим отсортировать по другой колонке полученные результаты. В данном сценарии фильтрация слетит, а это совсем не то, что мы ожидаем от контрола.
Для того, чтобы контрол «запомнил» настройки фильтрации нам потребуется обрабатывать оба события (фильтрация и сортировка).
Для этого служит нижеприведенный код:

gridDS.Filtering += new ObjectDataSourceFilteringEventHandler( gridDS_Filtering );
grid.Sorting += new GridViewSortEventHandler(grid_Sorting);

Логичным шагом после этого станет описание евент-хендлеров:

private void gridDS_Filtering(object sender, ObjectDataSourceFilteringEventArgs e)
{
ViewState[«FilterExpression»] = ((ObjectDataSourceView)sender).FilterExpression;
}

private void grid_Sorting(object sender, GridViewSortEventArgs e)
{
if (ViewState[«FilterExpression»] != null)
{
gridDS.FilterExpression = (string)ViewState[«FilterExpression»];
}
}

Мы используем свойства ViewState для хранения сведений по фильтрации, затем по полученному фильтрованному контенту производим сортировку.
Все работает, мы же ведь этого и добивались. Клево, не правда ли?
Ан-не совсем. Есть ньюанс. Как всегда, в общем-то.
Дело в том, что в случае, если пользователь пожелает отменить фильтрацию, кликнув на соответствующий пункт меню, то ничего не произойдет. Пустячок, а неприятно.
Решением данной проблемы станет очистка свойства ViewState. Делать это необходимо только после того, как пользователь действительно щелкнет на соответствующем пункте меню.
Суть в том, что обработка пользовательских действий в выпадающих менюшках производится через жаба-скриптовский(js) постбек, который передает серверу, что требуется сделать. Используются переменные __EVENTTARGET и __EVENTARGUMENT.
Для общего понимания объясню: когда вы фильтруете данные, скажем, по колонке Region, выставив искомое значение «Москва», переменная __EVENTARGUMENT принимает значение __SPGridView__;__Filter__;Region;Москва, а переменная __EVENTTARGET указывает на сам контрол. Когда пользователь щелкает мышью по пункту «Очистить фильтр» в выпадающем меню — значением __EVENTTARGET становится __SPGridView__;__Filter__;__ClearFilter__.
Для того, чтобы очистить ViewState потребуется переопределить метод LoadViewState:

protected sealed override void LoadViewState( object savedState )
{
base.LoadViewState( savedState );

if ( Context.Request.Form[«__EVENTARGUMENT»] != null)
Context.Request.Form[«__EVENTARGUMENT»].EndsWith( «__ClearFilter__» ) )
{
// Очищаем фильтрацию
ViewState.Remove( «FilterExpression» );
}
}

Наконец-то фильтрация работает так, как от нее ожидают.
Поздравляю, друзья, с победой! Можно пойти, налить себе холодненького… чего-нибудь.
Однако, что такое!? Как определить, к какой колонке был применен фильтр, а к какой нет?
В стандартных интерфейсах, обычно, отображается иконка рядом с названием колонки, которая дает пользователю понять, что производится фильтрация. Ну так давайте же и мы такую сделаем. И, заодно, обезопасим от того, чтобы время от времени пользователи прибегали вам с всклокоченными волосами и словами «у меня пропали нужные мне элементы!!!»

Такого рода иконки добавляются в событии RowDataBound, так что необходимо добавить евент-хендлер внутри метода CreateChildControls:

grid.RowDataBound += new GridViewRowEventHandler( grid_RowDataBound );

А затем и описать данный евент-хендлер.

private void grid_RowDataBound( object sender, GridViewRowEventArgs e )
{
if ( sender == null || e.Row.RowType != DataControlRowType.Header )
{
return;
}

SPGridView grid = sender as SPGridView;

if (String.IsNullOrEmpty(grid.FilterFieldName))
{
return;
}

// Отображение иконки в колонку, которой применен фильтр.
for (int i = 0; i < grid.Columns.Count; i++)
{
DataControlField field = grid.Columns[i];

if (field.SortExpression == grid.FilterFieldName)
{
Image filterIcon = new Image();
filterIcon.ImageUrl = «/_layouts/images/filter.gif»;
filterIcon.Style[HtmlTextWriterStyle.MarginLeft] = «2px»;

// Если просто добавить картинку и оставить, как есть, то иконка будет отображаться перед наименование колонки,
// а это не совсем так, как представлено в стандартных интерфейсах.
// Данный кусок кода поправит это недоразумение

Literal headerText = new Literal();
headerText.Text = field.HeaderText;

PlaceHolder panel = new PlaceHolder();
panel.Controls.Add(headerText);
panel.Controls.Add(filterIcon);

e.Row.Cells[i].Controls[0].Controls.Add(panel);

break;
}
}
}

На этом, по фильтрации, все. Надеюсь, что данный материал оказался Вам полезен.

Полезные ссылки:

SPGridView and SPMenuField: Displaying custom data through SharePoint lists
Using the SharePoint v3 Data Grid Control (SPGridView)
Filtering with SPGridView

Добавить комментарий