Элемент управления ListView был представлен в .Net Framework 3.5 как замена устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его предшественник, но в тоже время лишен некоторых внутренних механизмов, что впрочем целиком следствие из расширенной универсальности ListView. Среди отличий ListView и GridView можно назвать и гибкую настройку разметки, что позволяет выводить данные не только в табличном виде, но и вообще в любом каком пожелает программист. Благодаря шаблонам ItemTemplate, EditItemTemplate, InsertItemTeplate можно настроить внешний вид при любом из состояний ListView: редактировании или выборе элемента.
Я решил написать эту статью, чтобы поделиться опытом работы с ListView, привести некоторые способы решения общих задач, а также описать проблемы с которыми я столкнулся и которые у меня получилось решить не слишком красиво. Возможно, что при обсуждении статьи найдутся более гибкие решения описанных задач, чему я буду только рад.
В отличии от GridView, в ListView нет встроенной в редактор поддержки реализации механизма сортировки столбцов. На мой взгляд, отсутствие такой поддержки обусловлено тем, что ListView в отличии от GridView стал способен рендерить данные не только в табличном виде. Впрочем, даже при отсутствии механизма, добавление сортировки в таблицу ListView — это, наверное, самое легкое из того, что описано в этой статье. Рассмотрим реализацию на конкретном примере:
Здесь CommandArgument=«Bank.shortName» указывает на то, какой столбец из источника данных сортировать.
AlternatingItemTemplate предназначен для описания шаблона четных элементов списка. Это позволяет рендерить таблицы и другие типы наборов данных в красивом виде, например сделать так, что фоны строк данных в таблице будут чередоваться. В целом, отдельный шаблон — это конечно, хорошо, но чаще всего вся разница между строками в таблице заключается только в разном фоне или разных классах css. В этом случае объемный шаблон мне кажется избыточным и я предлагаю другое решение (подсмотренное в интернете):
В данном примере, вместо того, чтобы вводить в код AlternatingItemTemplate, я подставляю для четных строк особый css-класс. Кстати, в реальном проекте это позволяет сэкономить мне около 30 строк кода.
ListView как и GridView предлагает механизм выбора строки из набора данных. Выбор данных востребован во многих задачах по обработке информации и ListView предлагает кроме поддержки такого выбора еще и сильный механизм шаблонов. Что это значит? Это значит, что выбранные данные можно представить совсем не так как они были представлены в общем наборе данных. К примеру, можно расширить обычную строку записи и вывести дополнительные данные, вывести дополнительную панель инструментов по работе с данными, добавить дополнительные ссылки расширяющие представление о выбранных данных. Продемонстрирую пример:
Есть такая табличка:

А вот как выглядит результат выбора данных в ней:

При использовании ajax работа с такой табличкой для конечного пользователя становится удобной и приятной. Живой пример можно попробывать тут. Нетрудно заметить, что после выбора данных пользователь получает доступ к расширенной информации и к дополнительным сервисам, в даном случае к печати данных, но тут может быть и редактирование, скрытие, удаление данных, в случае когда с данными работает их создатель или лицо имеющее право на данные операции.
Я не буду приводить в данном примере строк кода, потому что реализация не выходит за рамки обычного, все реализуется в SelectedItemTemplate, в котром вместо одной строки tr в данным случае выводится три: старая жирным шрифтом, расширенная информация и панель инструментов.
Рассмотрим пример:

Весьма распространенная ситуация, когда необходимо объединить повторяющиеся по вертикали данные в одну ячейку. В GridView такая задача мной решалась через событие OnRowDataBound, в котором можно было получить доступ к текущей обрабатываемой строке и присвоить нужной ячейке нужный rowspan, а так же скрыть ненужные ячейки. В ListView, к сожалению, похожего инструмента нет. Более того на данный момент мне не известен аналогичный способ решения задачи. Предлагаемый мною способ строго говоря не реализует объединение ячеек в ListView, а только маскирует данные. Имеем такую разметку:
Здесь представлен кусочек tr в котором есть td, который и является целью объединения в случае повторяющихся данных. Предлагаю исходный текст метода GetRowspanVisible:
Код не претендует на изящество. Я принимаю любые конструктивные предложения по его переделке. Сразу скажу, что завязка на стили была выбрана из-за того, что через классы такой подход лично у меня не заработал. Итак: как можно видеть, при обработке ячеек в столбце им присваивается разный стиль, в зависимости от того, что требуется: скрыть или показать. Таким образом, пользователь увидит данные только в первой ячейке, и пустые поля в следующих. Результат можно посмотреть на рисунке выше. Минусом такого решения, кроме всего прочего, является, понятно, привязка отображаемых данных к верхней границе, так как эти данные являются элементом верхней строки и не содержат rowspan.
Хотел бы заметить, что решение далеко от идеала, даже я сам это понимаю. Но продолжительные поиски и консультации с камрадами ни к чему не привели, другого решения пока на руках нет. Буду рад увидеть альтернативы и с удовольствием избавлюсь от текущей реализации. Пока же, что есть, то есть. Жду ваших комментариев.
В принципе задача простая, но я все равно решил привести решение в данной статье. В listview при редактировании требуется выводить DropDownList который бы заполнялся данными из стороннего источника справочних данных и биндился к нашему источнику данных для listview. Пример:

Решение, как я уже сказал, простое:
Здесь dsMetalDic — справочные данные, Bind(«metalId») — биндинг к нашему источнику данных для ListView.
При удалении данных важно запросить у пользователя подтверждение на выполнение операции. При работе с ListView можно использовать следующее решение. Определим функцию javascript:
Используем ее в элементе который отвечает за удаление данных:
Порой необходимо вывести сразу весь набор данных состоящий из многих строк в режиме редактирования.

Решается эта задача через ItemTemplate и, в общем-то, это тривиальная задача. Другое дело как сохранить изменения во всех этих строках одним нажатием кнопки «Сохранить все»? Решение ниже:
Где lvEdit — это ListView.
Когда страница в таблице одна, то отображать Pager не имеет смысла, поэтому будем скрывать. Трудность состоит в том, что в стандартном варианте реализации постраничной разбивки данных доступа к элементу DataPager нет. Но мы найдем его и скроем следующим нехитрым способом:
Здесь rlv_DataBound — это обработчик события OnDataBound у ListView, dp — имя нашего DataPager, который «встроен» через шаблоны в ListView.
Сменить запрос для ListView не так уж и тривиально как может показаться когда мы используем LinqDataSource. Я знаю одно решение, которое приведу ниже:
Здесь dsResume_Selecting — это обработчик события OnSelecting у LinqDataSource.
В этой статье я постарался осветить многие аспекты работы с ListView, привел несколько примеров и предложил несколько решений стандартных и не очень задач. Впрочем, я понимаю, что многие задачи могут быть решены другим способом, возможно даже более интересным. Жду конструктивных предложений, альтернативных решений, комментариев и указаний на ошибки, которые вы найдете. Статья писалась продолжительное время, прошу прощения за возможные ошибки при наборе текста.
PS: извините за возможные глюки в подсветке синтаксиса, этот вопрос сейчас решается с саппортом Хабра.
Я решил написать эту статью, чтобы поделиться опытом работы с ListView, привести некоторые способы решения общих задач, а также описать проблемы с которыми я столкнулся и которые у меня получилось решить не слишком красиво. Возможно, что при обсуждении статьи найдутся более гибкие решения описанных задач, чему я буду только рад.
Содержание
- сортировка;
- отказ от AlternatingTemplate;
- выбор элемента;
- объединение ячеек в колонке;
- использование DropDownList для lookup при редактировании;
- запрос на удаление;
- редактирование и сохранение нескольких строк сразу;
- сокрытие DataPager;
- динамическая смена запросов для ListView.
Сортировка
В отличии от GridView, в ListView нет встроенной в редактор поддержки реализации механизма сортировки столбцов. На мой взгляд, отсутствие такой поддержки обусловлено тем, что ListView в отличии от GridView стал способен рендерить данные не только в табличном виде. Впрочем, даже при отсутствии механизма, добавление сортировки в таблицу ListView — это, наверное, самое легкое из того, что описано в этой статье. Рассмотрим реализацию на конкретном примере:
<th class=«bankTd»>
<asp:LinkButton ID=«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. В этом случае объемный шаблон мне кажется избыточным и я предлагаю другое решение (подсмотренное в интернете):
<ItemTemplate>
<tr <%# Container.DataItemIndex % 2 == 0? "" : " class=\"alt\"" %>>
* This source code was highlighted with Source Code Highlighter.
В данном примере, вместо того, чтобы вводить в код AlternatingItemTemplate, я подставляю для четных строк особый css-класс. Кстати, в реальном проекте это позволяет сэкономить мне около 30 строк кода.
Выбор элемента
ListView как и GridView предлагает механизм выбора строки из набора данных. Выбор данных востребован во многих задачах по обработке информации и ListView предлагает кроме поддержки такого выбора еще и сильный механизм шаблонов. Что это значит? Это значит, что выбранные данные можно представить совсем не так как они были представлены в общем наборе данных. К примеру, можно расширить обычную строку записи и вывести дополнительные данные, вывести дополнительную панель инструментов по работе с данными, добавить дополнительные ссылки расширяющие представление о выбранных данных. Продемонстрирую пример:
Есть такая табличка:

А вот как выглядит результат выбора данных в ней:

При использовании ajax работа с такой табличкой для конечного пользователя становится удобной и приятной. Живой пример можно попробывать тут. Нетрудно заметить, что после выбора данных пользователь получает доступ к расширенной информации и к дополнительным сервисам, в даном случае к печати данных, но тут может быть и редактирование, скрытие, удаление данных, в случае когда с данными работает их создатель или лицо имеющее право на данные операции.
Я не буду приводить в данном примере строк кода, потому что реализация не выходит за рамки обычного, все реализуется в SelectedItemTemplate, в котром вместо одной строки tr в данным случае выводится три: старая жирным шрифтом, расширенная информация и панель инструментов.
Объединение ячеек в колонке
Рассмотрим пример:

Весьма распространенная ситуация, когда необходимо объединить повторяющиеся по вертикали данные в одну ячейку. В GridView такая задача мной решалась через событие OnRowDataBound, в котором можно было получить доступ к текущей обрабатываемой строке и присвоить нужной ячейке нужный rowspan, а так же скрыть ненужные ячейки. В ListView, к сожалению, похожего инструмента нет. Более того на данный момент мне не известен аналогичный способ решения задачи. Предлагаемый мною способ строго говоря не реализует объединение ячеек в ListView, а только маскирует данные. Имеем такую разметку:
<ItemTemplate>
<tr runat=«server»>
<td class=«bankTd» runat=«server» id=«bankName» style='<%# GetRowspanVisible(Eval(«Bank.shortName»).ToString()) %>'>
<asp:HyperLink ID=«hlBank» runat=«server» Text='<%# Eval(«Bank.shortName») %>' NavigateUrl='test.aspx'></asp:HyperLink>
</td>
...* This source code was highlighted with Source Code Highlighter.
Здесь представлен кусочек tr в котором есть td, который и является целью объединения в случае повторяющихся данных. Предлагаю исходный текст метода GetRowspanVisible:
private string _bankName = "";
public string GetRowspanVisible(string bankName)
{
string result = «border: 0px; border-left: solid 1px #C0C0C0; font-size: 0px;»;
if (Request.Browser.Browser == «IE»)
result = «visibility: hidden; border: 0px; border-left: solid 1px #C0C0C0;»;
if (_bankName != bankName)
{
_bankName = bankName;
result = «border-top: solid 1px #C0C0C0; border-left: 1px; border-bottom: 0px;»;
}
return result;
}* This source code was highlighted with Source Code Highlighter.
Код не претендует на изящество. Я принимаю любые конструктивные предложения по его переделке. Сразу скажу, что завязка на стили была выбрана из-за того, что через классы такой подход лично у меня не заработал. Итак: как можно видеть, при обработке ячеек в столбце им присваивается разный стиль, в зависимости от того, что требуется: скрыть или показать. Таким образом, пользователь увидит данные только в первой ячейке, и пустые поля в следующих. Результат можно посмотреть на рисунке выше. Минусом такого решения, кроме всего прочего, является, понятно, привязка отображаемых данных к верхней границе, так как эти данные являются элементом верхней строки и не содержат rowspan.
Хотел бы заметить, что решение далеко от идеала, даже я сам это понимаю. Но продолжительные поиски и консультации с камрадами ни к чему не привели, другого решения пока на руках нет. Буду рад увидеть альтернативы и с удовольствием избавлюсь от текущей реализации. Пока же, что есть, то есть. Жду ваших комментариев.
Использование DropDownList для lookup при редактировании
В принципе задача простая, но я все равно решил привести решение в данной статье. В listview при редактировании требуется выводить DropDownList который бы заполнялся данными из стороннего источника справочних данных и биндился к нашему источнику данных для listview. Пример:

Решение, как я уже сказал, простое:
<asp:DropDownList ID=«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:
>* This source code was highlighted with Source Code Highlighter.
function OnDeleteClick()
{
return confirm('Удалить данные?');
}
Используем ее в элементе который отвечает за удаление данных:
<asp:LinkButton ID=«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 нет. Но мы найдем его и скроем следующим нехитрым способом:
protected void 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. Я знаю одно решение, которое приведу ниже:
protected void dsResume_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{
e.Result = Query;
}* This source code was highlighted with Source Code Highlighter.
Здесь dsResume_Selecting — это обработчик события OnSelecting у LinqDataSource.
Заключение
В этой статье я постарался осветить многие аспекты работы с ListView, привел несколько примеров и предложил несколько решений стандартных и не очень задач. Впрочем, я понимаю, что многие задачи могут быть решены другим способом, возможно даже более интересным. Жду конструктивных предложений, альтернативных решений, комментариев и указаний на ошибки, которые вы найдете. Статья писалась продолжительное время, прошу прощения за возможные ошибки при наборе текста.
PS: извините за возможные глюки в подсветке синтаксиса, этот вопрос сейчас решается с саппортом Хабра.