Советы и приемы при работе с ListView - Создание иерархического меню
ОГЛАВЛЕНИЕ
Создание иерархического меню
Многие веб-приложения предлагают вертикальное меню на правой или левой стороне страницы. Это меню позволяет пользователю переходить на страницы, на втором или более глубоком уровне вложения. Элемент управления ASP.NET Menu является приемлемым вариантом в этом случае. Однако я обычно использую элемент управления Menu, только когда у меня есть иерархический источник данных (обычно XML-файл) для заполнения меню, либо когда мне нужно создать всплывающие подменю.
Для статического многоуровневого списка элементов я предпочитаю элемент управления, подобный повторителю, для выдачи разметки, созданной группой разработки интерфейса пользователя. В ASP.NET 3.5 мой выбор среди подобных элементов управления – это ListView.
Рассмотрим меню, подобное представленному на рис. 1. Оно показано в шаблоне HTML CoffeeNCream, который можно бесплатно загрузить с oswd.org. На странице примера я просто включил разметку HTML в главную страницу ASP.NET.
Рис. 1. Стандартное меню
Исходный код HTML элемента правого меню должен выглядеть следующим образом:
<h1>Something</h1>
<ul>
<li><a href="#">pellentesque</a></li>
<li><a href="#">sociis natoque</a></li>
<li><a href="#">semper</a></li>
<li><a href="#">convallis</a></li>
</ul>
Как можно убедиться, он содержит строку верхнего уровня, за которой следует список ссылок. Первый элемент ListView используется для создания элементов H1, а вложенный ListView (или подобный ему привязанный к данным элемент управления) – для последующей визуализации списка ссылок. В качестве первого шага нужно взять данные для заполнения меню. В идеале для создания каждого элемента используется коллекция следующих объектов псевдотипов:
class MenuItem {
public string Title;
public Collection<Link> Links;
}
class Link {
public string Url;
public string Text;
}
Разумный метод заполнения коллекции MenuItem – это обработка информации из XML-файла. Ниже приведена возможная схема документа:
<Data>
<RightMenuItems>
<MenuItem>
<Title>Something</Title>
<Link url="..." text="pellentesque" />
:
</MenuItem>
</RightMenuItems>
</Data>
Ниже показано как использовать LINQ-XML для загрузки и обработки содержимого:
var doc = XDocument.Load(Server.MapPath("dataMap.xml"));
var menu = (from e in doc.Descendants("RightMenuItems")
select e).First();
var menuLinks = from mi in menu.Descendants("MenuItem")
select new
{
Title = mi.Value,
Links = (...)
};
После загрузки документа выбирается первый узел, именуемый RightMenuItems, после чего захватываются все его дочерние узлы MenuItem. Содержимое каждого узла MenuItem загружается в новый анонимный тип с двумя свойствами – Title («Заголовок») и Links («Ссылки»). Как заполнить коллекцию Links? Ниже приведен пример кода:
Links = (from l in mi.Descendants("Link")
select new {Url=l.Attribute("url").Value,
Text=l.Attribute("text").Value})
Следующее действие заключается в привязке этих составных данных к интерфейсу пользователя. Как уже упоминалось, внешний элемент управления ListView используется для визуализации заголовка, а второй, вложенный элемент управления ListView – для визуализации списка дочерних ссылок (см. рис. 2). Отметьте, что самый глубокий из вложенных элементов управления ListView должен быть привязан к данным с помощью метода Eval – другие подходы не сработают:
Рис. 2 Вложенные элементы ListView
<asp:ListView runat="server" ID="RightMenuItems"
ItemPlaceholderID="PlaceHolder2">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="PlaceHolder2" />
</LayoutTemplate>
<ItemTemplate>
<h1><%# Eval("Title") %></h1>
<asp:ListView runat="server" ID="subMenu"
ItemPlaceholderID="PlaceHolder3"
DataSource='<%# Eval("Links") %>'>
<LayoutTemplate>
<ul>
<asp:PlaceHolder runat="server" ID="PlaceHolder3" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li>
<a href='<%# Eval("Url") %>'><%# Eval("Text") %></a>
</li>
</ItemTemplate>
</asp:ListView>
</ItemTemplate>
</asp:ListView><asp:ListView runat="server" ID="subMenu"
ItemPlaceholderID="PlaceHolder3"
DataSource='<%# Eval("Links") %>'>
...
</asp:ListView>
Процесс привязки данных начинается с присоединения данных к ListView верхнего уровня. Когда это происходит, обрабатывается все тело ListView, включая вложенный элемент управления ListView. В теории, можно перехватить событие ItemDataBound родительского элемента управления ListView, пройти по дереву управления, взять ссылку на дочерний элемент управления ListView и программно привязать ее к данным. Если сделать это, исключения не возникнет, но привязывающая команда внутреннего элемента управления ListView будет утеряна, поскольку он запускается слишком поздно, чтобы повлиять не обработку. С другой стороны, выражение привязки данных автоматически вычисляется в течение любого события привязки данных точно в нужный момент жизненного цикла элемента управления. Это гарантирует верную привязку правильных данных к интерфейсу пользователя.