Элемент управления ListView был представлен в .Net Framework 3.5 как замена устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его предшественник, но в тоже время лишен некоторых внутренних механизмов, что впрочем целиком следствие из расширенной универсальности ListView. Среди отличий ListView и GridView можно назвать и гибкую настройку разметки, что позволяет выводить данные не только в табличном виде, но и вообще в любом каком пожелает программист. Благодаря шаблонам ItemTemplate, EditItemTemplate, InsertItemTeplate можно настроить внешний вид при любом из состояний ListView: редактировании или выборе элемента.
Я решил написать эту статью, чтобы поделиться опытом работы с ListView, привести некоторые способы решения общих задач, а также описать проблемы с которыми я столкнулся и которые у меня получилось решить не слишком красиво. Возможно, что при обсуждении статьи найдутся более гибкие решения описанных задач, чему я буду только рад.
Содержание
сортировка;
отказ от AlternatingTemplate;
выбор элемента;
объединение ячеек в колонке;
использование DropDownList для lookup при редактировании;
запрос на удаление;
редактирование и сохранение нескольких строк сразу;
сокрытие DataPager;
динамическая смена запросов для ListView.
Сортировка
В отличии от GridView, в ListView нет встроенной в редактор поддержки реализации механизма сортировки столбцов. На мой взгляд, отсутствие такой поддержки обусловлено тем, что ListView в отличии от GridView стал способен рендерить данные не только в табличном виде. Впрочем, даже при отсутствии механизма, добавление сортировки в таблицу ListView — это, наверное, самое легкое из того, что описано в этой статье. Рассмотрим реализацию на конкретном примере:
<thclass=«bankTd»> <asp: LinkButtonID=«sortBank»runat=«server»CommandName=«sort»CommandArgument=«Bank.shortName» Text=«Банк»/> </th> * This source code was highlighted with Source Code Highlighter.
Здесь CommandArgument=«Bank.shortName» указывает на то, какой столбец из источника данных сортировать.
Отказ от AlternatingItemTemplate
AlternatingItemTemplate предназначен для описания шаблона четных элементов списка. Это позволяет рендерить таблицы и другие типы наборов данных в красивом виде, например сделать так, что фоны строк данных в таблице будут чередоваться. В целом, отдельный шаблон — это конечно, хорошо, но чаще всего вся разница между строками в таблице заключается только в разном фоне или разных классах css. В этом случае объемный шаблон мне кажется избыточным и я предлагаю другое решение (подсмотренное в интернете):
В данном примере, вместо того, чтобы вводить в код AlternatingItemTemplate, я подставляю для четных строк особый css-класс. Кстати, в реальном проекте это позволяет сэкономить мне около 30 строк кода.
Выбор элемента
ListView как и GridView предлагает механизм выбора строки из набора данных. Выбор данных востребован во многих задачах по обработке информации и ListView предлагает кроме поддержки такого выбора еще и сильный механизм шаблонов. Что это значит? Это значит, что выбранные данные можно представить совсем не так как они были представлены в общем наборе данных. К примеру, можно расширить обычную строку записи и вывести дополнительные данные, вывести дополнительную панель инструментов по работе с данными, добавить дополнительные ссылки расширяющие представление о выбранных данных. Продемонстрирую пример:
Есть такая табличка:
А вот как выглядит результат выбора данных в ней:
При использовании ajax работа с такой табличкой для конечного пользователя становится удобной и приятной. Живой пример можно попробывать тут. Нетрудно заметить, что после выбора данных пользователь получает доступ к расширенной информации и к дополнительным сервисам, в даном случае к печати данных, но тут может быть и редактирование, скрытие, удаление данных, в случае когда с данными работает их создатель или лицо имеющее право на данные операции.
Я не буду приводить в данном примере строк кода, потому что реализация не выходит за рамки обычного, все реализуется в SelectedItemTemplate, в котром вместо одной строки tr в данным случае выводится три: старая жирным шрифтом, расширенная информация и панель инструментов.
Объединение ячеек в колонке
Рассмотрим пример:
Весьма распространенная ситуация, когда необходимо объединить повторяющиеся по вертикали данные в одну ячейку. В GridView такая задача мной решалась через событие OnRowDataBound, в котором можно было получить доступ к текущей обрабатываемой строке и присвоить нужной ячейке нужный rowspan, а так же скрыть ненужные ячейки. В ListView, к сожалению, похожего инструмента нет. Более того на данный момент мне не известен аналогичный способ решения задачи. Предлагаемый мною способ строго говоря не реализует объединение ячеек в ListView, а только маскирует данные. Имеем такую разметку:
Здесь представлен кусочек tr в котором есть td, который и является целью объединения в случае повторяющихся данных. Предлагаю исходный текст метода GetRowspanVisible:
Код не претендует на изящество. Я принимаю любые конструктивные предложения по его переделке. Сразу скажу, что завязка на стили была выбрана из-за того, что через классы такой подход лично у меня не заработал. Итак: как можно видеть, при обработке ячеек в столбце им присваивается разный стиль, в зависимости от того, что требуется: скрыть или показать. Таким образом, пользователь увидит данные только в первой ячейке, и пустые поля в следующих. Результат можно посмотреть на рисунке выше. Минусом такого решения, кроме всего прочего, является, понятно, привязка отображаемых данных к верхней границе, так как эти данные являются элементом верхней строки и не содержат rowspan.
Хотел бы заметить, что решение далеко от идеала, даже я сам это понимаю. Но продолжительные поиски и консультации с камрадами ни к чему не привели, другого решения пока на руках нет. Буду рад увидеть альтернативы и с удовольствием избавлюсь от текущей реализации. Пока же, что есть, то есть. Жду ваших комментариев.
Использование DropDownList для lookup при редактировании
В принципе задача простая, но я все равно решил привести решение в данной статье. В listview при редактировании требуется выводить DropDownList который бы заполнялся данными из стороннего источника справочних данных и биндился к нашему источнику данных для listview. Пример:
Решение, как я уже сказал, простое:
<asp: DropDownListID=«ddlMetals»runat=«server»DataSourceID=«dsMetalDic»DataTextField=«name»DataValueField=«id»SelectedValue=`<%# Bind(«metalId») %>`></asp: DropDownList> * This source code was highlighted with Source Code Highlighter.
Здесь dsMetalDic — справочные данные, Bind(«metalId») — биндинг к нашему источнику данных для ListView.
Запрос на удаление
При удалении данных важно запросить у пользователя подтверждение на выполнение операции. При работе с ListView можно использовать следующее решение. Определим функцию javascript:
> function OnDeleteClick() { return confirm(`Удалить данные?`); }
* This source code was highlighted with Source Code Highlighter. Используем ее в элементе который отвечает за удаление данных:
<asp: LinkButtonID=«DeleteButton»runat=«server»CommandName=«Delete»Text=«Удалить»OnClientClick=«return OnDeleteClick();»/> * This source code was highlighted with Source Code Highlighter.
Редактирование и сохранение нескольких строк сразу
Порой необходимо вывести сразу весь набор данных состоящий из многих строк в режиме редактирования.
Решается эта задача через ItemTemplate и, в общем-то, это тривиальная задача. Другое дело как сохранить изменения во всех этих строках одним нажатием кнопки «Сохранить все»? Решение ниже:
foreach (var item in lvEdit.Items) { lvEdit.UpdateItem(item.DataItemIndex, true); }* This source code was highlighted with Source Code Highlighter.
Где lvEdit — это ListView.
Сокрытие DataPager
Когда страница в таблице одна, то отображать Pager не имеет смысла, поэтому будем скрывать. Трудность состоит в том, что в стандартном варианте реализации постраничной разбивки данных доступа к элементу DataPager нет. Но мы найдем его и скроем следующим нехитрым способом:
protectedvoid rlv_DataBound(object sender, EventArgs e) { ListView listView = sender as ListView; if (listView != null) { DataPager pager = listView.FindControl(«dp») as DataPager; if (pager != null) pager.Visible = pager.PageSize < pager.TotalRowCount; } }* This source code was highlighted with Source Code Highlighter.
Здесь rlv_DataBound — это обработчик события OnDataBound у ListView, dp — имя нашего DataPager, который «встроен» через шаблоны в ListView.
Динамическая смена запросов для ListView
Сменить запрос для ListView не так уж и тривиально как может показаться когда мы используем LinqDataSource. Я знаю одно решение, которое приведу ниже:
protectedvoid dsResume_Selecting(object sender, LinqDataSourceSelectEventArgs e) { e.Result = Query; }* This source code was highlighted with Source Code Highlighter.
Здесь dsResume_Selecting — это обработчик события OnSelecting у LinqDataSource.
Заключение
В этой статье я постарался осветить многие аспекты работы с ListView, привел несколько примеров и предложил несколько решений стандартных и не очень задач. Впрочем, я понимаю, что многие задачи могут быть решены другим способом, возможно даже более интересным. Жду конструктивных предложений, альтернативных решений, комментариев и указаний на ошибки, которые вы найдете. Статья писалась продолжительное время, прошу прощения за возможные ошибки при наборе текста.