Использование XML и LINQ в элементах управления TreeView и ListView

Источник: cyberguru
Miroslav Kadera

В то время как все больше информации хранится в XML-формате, веб-приложениям необходимо иметь возможность в интерфейсе веб-страницы осуществлять просмотр и редактирование информации, хранимой в XML-файле. Если информация хранится в файле и имеет табличный вид, то мы можем использовать элементы управления DataGrid, GridView и Repeater. Но что делать, если XML-данные представлены  более иерархической структурой? Представьте телефонную книгу компании, которая рекурсивно структурируется в филиалы и отделы. Как такую информацию, которая может иметь множество узлов и любое количество уровней, отобразить и отредактировать посредством веб-страницы?

В данной статье мы создадим веб-страницу, которая будет отображать содержимое телефонной книги всей компании, где информация находится в XML-файле. Страница рекурсивно будет отображать XML-информацию из телефонной книги при помощи TreeView для того, чтобы перечислять филиалы и отделы, а также ListView - для перечисления сотрудников, принадлежащих выбранному филиалу или отделу. В частности, ListView отобразит сотрудников, принадлежащих выбранному филиалу, а также тех, кто принадлежит всем отделам данного филиала. (В следующей статье мы рассмотрим способ расширения возможностей ListView, позволив пользователю добавлять, редактировать и удалять записи в телефонной книге.) Читайте далее, чтобы узнать больше об этом!

 

Рабочий пример приложения доступен в конце статьи

На протяжении данной статьи мы будет рассматривать различные блоки кода и скриншоты приложения. Для этого было использовано приложение, которое вы можете загрузить в конце статьи. Я рекомендую вам скачать приложение, загрузить его в Visual Studio и запустить локально, чтобы увидеть все в действии. После этого можете вернуться к данной статье и следовать действиям, выполняемым в ней.

Обратите внимание на то, что многие элементы управления и принципы, обсужденные в статье стали доступны в ASP.NET версии 3.5 - поэтому, приложение не будет работать в Visual Studio 2005 и в предыдущих версиях. Если у вас нет Visual Studio 2008, то вы всегда можете загрузить и установить бесплатную версию Visual Web Developer 2008, которая может быть установлена наряду с другими версиями Visual Studio.

Исследование источника XML-данных

Данная статья демонстрирует способ отображения XML-данных в веб-странице при помощи элементов управления TreeView и ListView. До того как мы приступим к созданию страницы, давайте уделим пару минут исследованиию структуры XML-данных, которые мы собираемся отображать. Следующая XML-структура представляет собой телефонную книгу сотрудников компании. Компания состоит из филиалов и отделов. Филиал может состоять из отделов, а они, в свою очередь, из других отделов. Сотрудники могут принадлежать любому филиалу или отделу.

XML-данные могут выглядеть следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<PhoneBook>
   <Branch id="1" name="Northern Branch">
      <Department id="1" name="Marketing">
         <Employee id="1" name="Miroslav" telephone="555-5555" />
         <Employee id="2" name="Scott" telephone="555-1111" />
         <Department id="2" name="Advertising">
            <Employee id="3" name="Chris" telephone="555-2222" />
            <Employee id="4" name="Bruce" telephone="555-3333" />
            <Employee id="5" name="Sam" telephone="555-4444" />
         </Department>
      </Department>
      <Employee id="6" name="Jisun" telephone="555-9999" />
   </Branch>
   <Department id="3" name="Executive Team">
      <Employee id="7" name="Davis" telephone="555-8888" />
      <Employee id="8" name="Kate" telephone="555-9900" />
   </Department>
</PhoneBook>

Указанный выше XML код демонстрирует то, что существует северный филиал (Northern Branch) с отделом маркетинга (Marketing). Сотрудники Miroslav и Scott работают в отделе Marketing. Отдел рекламы (Advertising) и принадлежащий ему отдел маркетинга (Marketing) являются местами где работают Chris, Bruce и Sam. Jisun работает в северном филиале (Northern Branch), но он не принадлежит какому-то определенному отделу данного филиала. В отделе исполнительных директоров (Executive Team) работают Davis и Kate - они также не принадлежат ни одному из филиалов.

Заполнение элемента управления TreeView XML-данными

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

Элемент управления XmlDataSource позволяет вам с легкостью отображать XML-данные в TreeView. Начните с добавления XmlDataSource на страницу. Далее, установите его свойство DataFile таким образом, чтобы оно указывало на XML-файл источник:

<asp:XmlDataSource ID="treeSource" runat="server" DataFile="~/PhoneBook.xml" /> 

Сам по себе XmlDataSource не отображает ничего. Он просто осуществляет запрос XML-данных. Чтобы вывести информацию нам нужно добавить элемент управления TreeView и привязать его к XmlDataSource. Перетащите элемент TreeView из Toolbox на страницу и установите его свойство DataSourceID в ID элемента XmlDataSource (treeSource). Далее, создайте TreeNodeBinding для корневого элемента PhoneBook и TreeNodeBindings для дочерних элементов Branch и Department. TreeNodeBinding указывает TreeView какой XML-элемент и как он должен отобразить.

<asp:TreeView ID="tvwPhoneBook" runat="server" DataSourceID="treeSource" 
   AutoGenerateDataBindings="False">
   <DataBindings>
       <asp:TreeNodeBinding DataMember="PhoneBook" Text="Phone Book" />
            <asp:TreeNodeBinding DataMember="Branch"
                   FormatString="Branch "{0}"" TextField="name" />
            <asp:TreeNodeBinding DataMember="Department"
                   FormatString="Dpt. "{0}"" TextField="name" />
   </DataBindings>
</asp:TreeView> 

Запустив страницы мы можем увидеть TreeView с узлами, соответствующими XML-структуре филиалов и отделов. Обратите внимание на то, что TreeView не включает в себя элементы Employee. Это потому, что в TreeNodeBinding нет элементов Employees.

Использование XML и LINQ в элементах управления TreeView и ListView - ASP.NET - Microsoft .NET - Программирование, исходники, операционные системы

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

Когда 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.

Использование XML и LINQ в элементах управления TreeView и ListView - ASP.NET - Microsoft .NET - Программирование, исходники, операционные системы

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

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

Добавление возможности сортировки и перелистывания

Элемент управления ListView может предоставить пользователю возможность сортировать содержимое. ListView обрабатывает всю логику сортировки - нам всего лишь нужно добавить элемент управления LinkButton (или Button, либо ImageButton) в ListView, установить его свойства CommandName и CommandArgument в Sort и указать соответствующее название поля, по которому нам необходимо осуществить сортировку. К примеру, чтобы добавить LinkButton для сортировки результатов по имени (свойству Name), используйте следующий LinkButton:

<asp:LinkButton ID="lnkSortName" runat="server" Text="Name" CommandName="Sort" CommandArgument="Name" /> 

Другим полезным улучшением является простая реализация возможности перелистывания содержимого. ASP.NET 3.5 включает в себя элемент управления DataPager, который может быть использован вместе с элементом ListView чтобы предоставить перелистываемый интерфейс. После добавления элемента управления DataPager на страницу установите его свойство PageSize в количество записей, которые вы хотите отобразить на одной странице. Далее установите свойство PagedControlID в ID элемента управления ListView, чьи данные будет возможно перелистать. Последним шагом будет указание полей для осуществления перелистывания (либо это будут ссылки Следующая/Предыдущая (Next/Previous), либо номера страниц в качестве ссылок и т.д.) - все остальное будет обработано элементами управления DataPager и ListView! (Для получения более подробной информации читайте статью про перелистывание посредством элементов управления ListView и DataPager)

Следующее изображение демонстрирует ListView после того, как он был настроен на поддержку функциональности сортировки и перелистывания.

Использование XML и LINQ в элементах управления TreeView и ListView - ASP.NET - Microsoft .NET - Программирование, исходники, операционные системы

Редактирование информации

ASP.NET включает в себя множество инструментов для отображения и редактирования XML-документов. Предыдущая статья по теме отображения информации демонстрировала способ создания веб-страницы, отображающей содержимое иерархического XML-файла при помощи элементов управления TreeView, ListView, XmlDataSource, LinqDataSource и примерно 50 строк кода. В частности, страница отображала содержимое фиктивной телефонной книжки с номерами сотрудников, которая позволяла хранить неопределенное число сотрудников,  вложенных в отделы и филиалы компании. Элементы управления TreeView и XmlDataSource отображали различные филиалы и отделы, в то время как ListView и LinqDataSource отображали сотрудников, принадлежащих к выбранному отделу или филиалу.

В дополнение к отображению информации ListView и LinqDataSource могут быть расширены таким образом, чтобы они поддерживали редактирование, вставку и удаление информации. Данная статья исследует способ обновления ListView и кода, тем самым позволяя посетителям добавлять, редактировать и удалять записи о телефонах сотрудников. Результатом данной статьи будет веб-страница, которая отображает XML-информацию и позволяет пользователю обновлять ее из простого, легкого в использовании, интерфейса веб-страницы. Читайте далее, чтобы узнать больше об этом!

Настройка ListView для поддержки редактирования

Элемент управления ListView, предоставленный в ASP.NET версии 3.5, отображает серию записей из источника данных при помощи шаблонов. В дополнение к выводу информации элемент управления ListView также может быть использован для вставки, редактирования и удаления информации. Как и элемент GridView, элемент управления ListView позволяет редактирование "по месту". То есть, пользователь может выбрать определенную запись на редактирование из отображенного списка записей, выполнить все необходимые изменения и затем обновить информацию. ListView также предоставляет функциональность вставки.

Принципы редактирования записей в ListView просты. Нам необходимо создать специальный шаблон для редактируемого элемента - данный шаблон, EditItemTemplate, используется для обработки элемента, редактируемого пользователем. В дополнение к этому нам необходимо добавить элемент управления Button, LinkButton или ImageButton (обычно в ItemTemplate), у которого свойство CommandName установлено в "Edit". При нажатии на данную кнопку выполняется постбэк и ListView повторно привязывается к своему источнику данных, при этом данный элемент обрабатывается при помощи EditItemTemplate.

ItemTemplate достаточно прост - просто добавьте LinkButton (Button или ImageButton) со свойством CommandName установленным в "Edit":

<ItemTemplate>
   <tr>
      <td>
         <asp:LinkButton ID="lnkEdit" runat="server" CommandName="Edit" Text="Edit" />
      </td>
      <td>
         <%# Eval("Name") %>
      </td>
      <td>
         <%# Eval("Telephone") %>
      </td>
   </tr>
</ItemTemplate> 

До того как мы создадим EditItemTemplate для ListView, существует усложняющее обстоятельство в нашей ситуации - нам необходим для начала  адрес. Вспомните, ведь отображаемые в ListView записи могут быть расположены на разных "уровнях" в XML-файле (т.е. различных филиалах/отделах). К примеру, при выборе определенного филиала или отдела из TreeView, ListView отображает не только сотрудников данного филиала или отдела, но и всех сотрудников дочерних филиалов и отделов. Необходимо иметь возможность определить правильный филиал или отдел,  которому принадлежит данный сотрудник, при сохранении измененных значений обратно в XML-файл.

Мы можем решить данную проблему путем запоминания адреса XPath XML-элемента Employee при загрузке его в ListView. Для этого нам необходимо добавить свойство в наш анонимный тип, возвращенный запросом LINQ. Текст в красном цвете XPathAddress: демонстрирует новый анонимный тип,

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

В дальнейшем мы установим данное значение в качестве свойства CommandArgument элемента LinkButton "Update" в EditItemTemplate элемента ListView.

Как вы можете заметить в указанном выше LINQ-запросе, XPathAddress было назначено значение, возвращенное методом getXPathAddress, которое нам необходимо создать в пределах класса с фоновым кодом ASP.NET-страницы. Метод итеративно пройдет по переданной структуре employeeElement к корневому элементу, тем самым выстраивая значение XPathAddress поднимаясь по дереву:

private string getXPathAddress(XElement element)
{
   XElement cElement = element;
   string xPath = "";

   // Мы итеративно пройдемся по структуре к корневому элементу
   // Цикл завершится в корневом элементе (мы не включаем корневой элемент
   // в адрес!!)
   while (cElement.Parent != null)
   {
       xPath = String.Format("/*[@id='{0}']",
               cElement.Attribute("id").Value)
                + xPath;

       cElement = cElement.Parent;
    }

    return xPath;

Теперь мы знаем выражение XPath для каждого объекта сотрудника и мы можем создать EditItemTemplate. Как вы могли бы уже заметить, свойству CommandArgument элемента LinkButton "Update" назначается значение XPathAddress , возвращенное LinqDataSource. Нам необходимо использовать данное значение в коде для обновления соответствующей записи сотрудника. Также обратите внимание на то, что вместо отображения значений имени и телефона сотрудника в Label мы используем веб-элемент управления TextBox.

<EditItemTemplate>
   <tr>
      <td>
         <asp:LinkButton ID="lnkUpdate" runat="server"   
            CommandName="XUpdate"
            CommandArgument="<%# Eval("XPathAddress") %>"
            Text="Update" />,
         <asp:LinkButton ID="lnkCancelEdit" runat="server"
            CommandName="Cancel"
            Text="Cancel" />
      </td>
      <td>
         <asp:TextBox ID="txtEditName" runat="server"
                   Text="<%# Eval("Name") %>" />
      </td>
      <td>
         <asp:TextBox ID="txtEditTelephone" runat="server"
                   Text="<%# Eval("Telephone") %>" />
      </td>
   </tr>
</EditItemTemplate> 

Вам наверняка интересно, почему мы использовали значение CommandName "XUpdate" вместо "Update". Если мы установим CommandName в "Update", то ListView попробует обновить изменения, автоматически вызвав метод Update элемента управления источником данных. Мы сопоставили данные собственноручно (в обработчике события Selecting элемента управления LinqDataSource), так что наш LinqDataSource в данном случае не может служить в качестве обновляющего источника. Поэтому нам необходимо самим написать метод, чтобы сохранить изменения.

Сохранение отредактированной записи в XML-файле

Нам необходимо выполнить код при нажатии на кнопку "XUpdate" (LinkButton). Событие ItemCommand элемента ListView вызывается, когда нажата кнопка со свойством CommandName. Поэтому нам необходимо создать обработчик для данного события. Помните, что данный обработчик события выполнится, когда будет нажата любая кнопка команды в ListView - это включает в себя и кнопки сортировки и перелистывания данного интерфейса. Следовательно, нам обязательно нужно проверить передаваемое значение e.CommandName  на предмет того, чтобы оно было равно "XUpdate" до того, как мы будем продолжать.

Когда будет нажата кнопка "XUpdate" мы должны будем сохранить изменения и возвратить ListView в его предыдущее состояние. Следующий код демонстрирует данную функциональность. Обратите внимание на то, что информация сохранена методом saveChanges - мы очень скоро создадим данный метод .

protected void lvwEmployees_ItemCommand(object sender, ListViewCommandEventArgs e)
{
   switch (e.CommandName)
   {
      // Обновление XML-файла после редактирования
      case "XUpdate":
         saveChanges((string)e.CommandArgument, e.Item);

         lvwEmployees.EditIndex = -1;
         lvwEmployees.DataBind();

         break;
   }

Нашим последним заданием по отношению к редактированию записей  является создание метода saveChanges, который сохраняет изменения в XML-файле. Как это демонстрирует указанный выше код, методу saveChanges передается два параметра:

  • Адрес XPath изменяемого элемента (не забудьте, что мы назначили данное значение XPathAddress свойству CommandArgument кнопки Update )
  • Редактируемый элемент ListViewItem . Наш метод будет искать TextBoxes с новыми значениями данного ListViewItem

Метод saveChanges загружает XML-элемент Employees из файла источника в качестве объекта XElement. Значения объекта XElement затем будут модифицированы чтобы соответствовать пользовательскому вводу. Наконец, обновленный объект XElement сохраняется в XML-файле.

protected void saveChanges(string xPath, ListViewItem lvwItem)
{
   string fileName = MapPath("PhoneBook.xml");

   XElement rootElement = XElement.Load(fileName);
   XElement itemElement = rootElement.XPathSelectElement(xPath);

   itemElement.SetAttributeValue("name",
         ((TextBox)lvwItem.FindControl("txtEditName")).Text);

   itemElement.SetAttributeValue("telephone",
         ((TextBox)lvwItem.FindControl("txtEditTelephone")).Text);

   rootElement.Save(fileName);

И это все! На данном этапе пользователь может редактировать существующие записи о телефонах сотрудников, изменяя их имя, номер телефона или  и то, и другое.

Удаление записей номеров телефонов сотрудников

Удаление номера телефона сотрудника работает по тому же принципу. Нам необходимо запомнить XPathAddress для сотрудников и передавать его обработчику события, ответственному за удаления записей. Как и в случае с редактированием, нам необходимо добавить кнопку Delete LinkButton (Button или ImageButton) к ItemTemplate элемента ListView. Установите его свойство CommandName в "XDelete" и свойство CommandArgument в значение XPathAddress удаляемого элемента.

<asp:LinkButton ID="lnkDelete" runat="server"
      CommandName="XDelete"
      CommandArgument='<%# Eval("XPathAddress") %>'
      Text="Delete" /> 

В обработчике события ItemCommand элемента управления ListView нам необходимо обработать команду "XDelete". Как и в случае с командой "XUpdate", мы вызовем вспомогательный метод (deleteItem), передавая ему выражение XPath и затем  возвращая ListView в исходное состояние.

case "XDelete":
   deleteItem((string)e.CommandArgument);

   lvwEmployees.EditIndex = -1;
   lvwEmployees.DataBind();

   break; 

Метод deleteItem загружает объект XElement, основанный на предоставленном выражении XPath, удаляет его и затем сохраняет содержимое XML-файла.

protected void deleteItem(string xPath)
{
   string fileName = MapPath("PhoneBook.xml");

   XElement rootElement = XElement.Load(fileName);

   XElement itemElement = rootElement.XPathSelectElement(xPath);

   itemElement.Remove();
   rootElement.Save(fileName);

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

Нашим последним заданием будет предоставление пользователю возможности добавления новых записей о номерах телефонов сотрудников к выбранному филиалу или отделу. Вставка элемента из ListView является задачей схожей с редактированием записи - оба интерфейса определяются посредством шаблонов. Чтобы указать интерфейс для вставки, используйте InsertItemTemplate. Данный шаблон может быть отображен в качестве первого или последнего элемента в ListView.

InsertItemTemplate (продемонстрирован ниже) выглядит так же для EditItemTemplate в том отношении, что он содержит TextBox для имени и номера телефона сотрудника. Вместо кнопки Update у нас будет кнопка Save (чье свойство CommandName установлено "XSave").

<InsertItemTemplate>
   <tr>
      <td style="border-bottom: solid black 1px;">
         <asp:LinkButton ID="lnkSave" runat="server" CommandName="XSave" Text="Save" />,
         <asp:LinkButton ID="lnkCancelInsert" runat="server" CommandName="Cancel" Text="Cancel" />
      </td>
      <td style="border-bottom: solid black 1px;">
          <asp:TextBox ID="txtInsertName" runat="server" Text='<%# Eval("Name") %>' />
      </td>
      <td style="border-bottom: solid black 1px;">
         <asp:TextBox ID="txtInsertTelephone" runat="server" Text='<%# Eval("Telephone") %>' />
      </td>
   </tr>
</InsertItemTemplate> 

Как и в случае с командами "XUpdate"и "XDelete", нам необходимо обработать команду "XSave" в обработчике события ItemCommand ListView. После сохранения элемента (посредством вызова saveNewItem), нам необходимо повторно привязать информацию к ListView, поэтому только что добавленный в список элемент будет отображен.

case "XSave":
   saveNewItem(e.Item);

   lvwEmployees.DataBind();
   break; 

Метод saveNewItem более сложен, чем методы saveChanges или deleteItem. Часть препятствий исходит из того факта, что каждый элемент в XML-файле имеет атрибут id с уникальным идентификатором для этого "уровня". Так что при добавлении нового элемента нам необходимо выбрать соответствующее значениеid.

Метод saveNewItem выполняет следующие действия:

  1. Загрузить корневой элемент (как и в других методах)
  2. Получить XPathAddress выбранного в TreeView элемента и найти элемент в XML-файле
  3. Получить максимальное значение ID из всех элементов в корневом элементе
  4. Создать новый объект XElement
  5. Добавить новый элемент к его родителю и сохранить в файле

Следующий код демонстрирует это :

protected void saveNewItem(ListViewItem lvwItem)
{
   string fileName = MapPath("PhoneBook.xml");

   XElement rootElement = XElement.Load(fileName);

   string xPath = tvwPhoneBook.SelectedNode.DataPath.Substring(tvwPhoneBook.Nodes[0].DataPath.Length);

   XElement parentElement = rootElement.XPathSelectElement(xPath);

   int maxId = parentElement.Elements().Max(c => int.Parse(c.Attribute("id").Value));

   XElement newElement = new XElement("Employee",
            new XAttribute("id", (maxId + 1).ToString()),
            new XAttribute("name", ((TextBox)lvwItem.FindControl("txtInsertName")).Text),
            new XAttribute("telephone", ((TextBox)lvwItem.FindControl("txtInsertTelephone")).Text));

   parentElement.Add(newElement);

   rootElement.Save(fileName);

И все! Небольшим усилием и парочкой строк кода мы создали работоспособное приложение ASP.NET для редактирования структурированной XML-информации. Главной идеей, на которой основано приложение, является связь между TreeView и ListView, что дало нам возможность редактировать табличные данные, хранимые в более иерархической XML-структуре. Большая часть работы выполняется за нас элементами TreeView, ListView, DataPager и LinqDataSource. Все что нам необходимо было сделать, так это написать код, который обрабатывает позицию записи в различных уровнях XML-структуры.

Использование XML и LINQ в элементах управления TreeView и ListView - ASP.NET - Microsoft .NET - Программирование, исходники, операционные системы

Веселого программирования!


Страница сайта http://185.71.96.61
Оригинал находится по адресу http://185.71.96.61/home.asp?artId=22297