Использование XML и LINQ в элементах управления TreeView и ListView - Загрузка записей о сотрудниках выбранного филиала или отдела

ОГЛАВЛЕНИЕ

Загрузка записей о сотрудниках выбранного филиала или отдела

Когда TreeView правильно отобразит филиалы и отделы, нашим следующим заданием будет вывод сотрудников выбранного филиала или отдела в правой части TreeView. Мы будем использовать LinqDataSource для того, чтобы загрузить информацию о сотрудниках из XML-данных телефонной книги. Начните с добавления элемента управления LinqDataSource к вашей странице:

<asp:LinqDataSource ID="listSource" runat="server" />   

Когда данные будут запрошены из LinqDataSource будет вызвано событие Selecting. При выполнении данного события нам необходимо программно получить соответствующий набор записей сотрудников по выбранному филиалу или отделу, поэтому создайте обработчик для события Selecting элемента LinqDataSource. Для того, чтобы создать данный обработчик события, щелкните дважды по элементу LinqDataSource в дизайнере страницы. Это создаст обработчик события в фоновом классе вашей страницы, названный listSource_Selecting и выглядящий  следующим образом :

protected void listSource_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{

}

В пределах данного метода нам необходимо выбрать информацию о сотрудниках (из XML-файла-источника), которые соответствуют выбранному узлу в TreeView. Выбранный в TreeView узел может быть получен при помощи свойства SelectedNode элемента TreeView, которое возвращает соответствующий экземпляр TreeNode выбранному узлу. Возвращенный TreeNode включает в себя свойство DataPath, которое диктует путь к элементу, представляемому TreeNodeXPath-запроса. (XPath - это язык для осуществления запросов к узлам в пределах XML-документа.) в такой форме, что он может быть сразу же использован в качестве

К примеру, TreeNode представляющий первый отдел первого филиала (отдел маркетинга (Marketing) филиала Northern Branch) имеет значение свойства DataPath равное /*[position()=1]/*[position()=1]/*[position()=1]. Первая часть, /*[position()=1], представляет собой корневой элемент. Если перевести на русский , то это означает "дайте мне первый узел (position()=1) , который начинает корень". Вторая часть возвращает первый филиал, потому что, как и в первой части, она возвращает первый узел (position()=1), но на этот раз это будет первый узел корневого элемента (поскольку это то, что мы вернули при помощи первой части выражения XPath). Наконец, последняя часть представляет собой первый отдел первого филиала. Как вы уже могли заметить, XPath-индексирование, используемое position(), начинается с 1, а не с 0. (Для получения более подробной информации посетите уроки по XPath на сайте W3 Schools.)

Чтобы получить соответствующий набор XML-элементов в событии Selecting элемента LinqDataSource мы будем использовать LINQ to XML. LINQ to XML является серией классов, появившихся в .NET Framework версии 3.5, которые способствуют работе с XML-данными. Одним из основных классов в LINQ to XML является XElement, который представляет XML-элемент. XElement также включает в себя метод Load , который мы можем вызвать для того, чтобы считывать содержимое XML-документа:

XElement rootElement = XElement.Load(MapPath("PhoneBook.xml"));   

Обратите внимание на то, что метод Load из LINQ to XML возвращает корневой элемент. Если вы раньше уже работали с XML-данными в предыдущих версиях .NET Framework , то вы наверняка использовали класс XmlDocument, чей метод LoadChildNode. Мы должны помнить об этом при осуществлении запроса с XPath потому, что нам надо вырезать первую часть (адрес XPath к корневому элементу) из строки XPath. считывает весь документ, тем самым корневой элемент находится в первом

Следующий код демонстрирует все это - он начинает со считывания в запросе XPath на наличие SelectedNode элемента TreeView и затем использует метод Substring для того, чтобы убрать первые n символов из строки XPath, где n является длиной XPath-выражения корневого элемента.

// Получение XPath-адреса выбранного филиала или отдела
string xPathQuery = tvwPhoneBook.SelectedNode.DataPath;

// Обрезание пути корневого элемента из XPath-запроса
string rootElementXPath = tvwPhoneBook.Nodes[0].DataPath;
xPathQuery = xPathQuery.Substring(rootElementXPath.Length); 

Linq to XML предоставляет XElement с тремя методами: XPathEvaluate, XPathSelectElement и XPathSelectElements. Все три метода принимают XPath-выражение в качестве входного параметра. Как можно понять по их названиям, XPathEvaluateXPathSelectElement возвращает XElement , указанный выражением XPath , переданным в метод; и XPathSelectElements , который возвращает набор элементов. Мы будем использовать второй метод, XPathSelectElement. используется для определения выражения XPath, возвращая при этом скалярное значение такое, как значение атрибуты или текстовое содержимое XML-элемента;

XElement parentElement = rootElement.XPathSelectElement(xPathQuery);   

Все немного усложняется тем, что нам необходимо выбрать записи сотрудников при помощи рекурсии. К примеру, если у нас есть филиал, выбранный в TreeView, то нам необходимо отобразить сотрудников всех отделов данного филиала , а также сотрудников филиалов, которые не принадлежат ни одному из отделов. Класс XElement содержит метод Descendants(elementName) , который может помочь в этом деле. Поэтому мы можем выбрать всех сотрудников из parentElement, независимо от глубины иерархии, при помощи:

parentElement.Descendants("Employee")

(Чтобы осуществить просмотр сотрудников без рекурсии - то есть, просто получить сотрудников выбранного филиала или отдела - используйте метод Elements(elementName).)

Нас интересуют только имена  и номера телефонов сотрудников. Простейшим способом (и более элегантным) вывода данной информации будет использование анонимного типа данных. Наш LINQ-запрос будет выглядеть следующим образом:

var query = from employeeElement in parentElement.Descendants("Employee")
            select new
            {
                Name = employeeElement.Attribute("name").Value,
                Telephone = employeeElement.Attribute("telephone").Value
            }; 

Все что нам остается, так это вернуть информацию в LinqDataSource из события Selecting. Это выполнимо при помощи назначения результатов запроса параметру e.Results.

e.Result = query;   

Отображение выбранных сотрудников в элементе управления ListView
Теперь, когда мы создали LINQ-запрос для получения сотрудников, которые принадлежат выбранному филиалу или отделу, нам остается только лишь отобразить их в элементе управления ListView. Добавьте элемент управления ListView на страницу и привяжите его к LinqDataSource.

<asp:ListView ID="lvwEmployees" runat="server" DataSourceID="listSource" />  

Элемент управления ListView работает на основе шаблонов. Нам необходимо определить шаблон для отображения всего ListView (LayoutTemplate) и затем шаблон для обработки индивидуальных элементов (ItemTemplate). Добавьте следующее объявление шаблона к вашему элементу управления ListView.

<LayoutTemplate>
   <table>
      <tr>
         <td style="font-weight: bold;">Name</td>
         <td style="font-weight: bold;">Phone number</td>
      </tr>
      <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
   </table>
</LayoutTemplate>

<ItemTemplate>
   <tr>
      <td><%# Eval("Name") %></td>
      <td><%# Eval("Telephone") %></td>
   </tr>
</ItemTemplate> 

Указанная выше разметка шаблона отображает результаты в HTML таблице (<table>) с двумя колонками. Чтобы получить имя и номер телефона каждого сотрудника используйте функцию привязки данных Eval("propertyName"). Конечным результатом будет страница, отображающая набор сотрудников в ListView в правой части элемента управления TreeView.


Нам необходимо убедиться в том, что содержимое ListView будет повторно привязано к нему каждый раз когда пользователь выберет новый филиал или отдел из TreeView. Это реализуемо при помощи вызова метода ListView.DataBind() после каждого изменения SelectedNode элемента TreeView:

protected void tvwPhoneBook_SelectedNodeChanged(object sender, EventArgs e)
{
   lvwEmployees.DataBind();
}