Фильтрация "Основной/подробности" с помощью двух элементов управления DropDownList (VB)

Скотт Митчелл

Загрузить PDF-файл

В этом руководстве расширена связь master/подробностей, чтобы добавить третий уровень, используя два элемента управления DropDownList для выбора нужных записей родителей и бабушек и дедушек.

Введение

В предыдущем руководстве мы рассмотрели, как отобразить простой отчет master/details с помощью одного раскрывающегося списка, заполненного категориями и GridView, в котором отображаются продукты, относящиеся к выбранной категории. Этот шаблон отчета хорошо работает при отображении записей, которые имеют отношение "один ко многим" и могут быть легко расширены для работы в сценариях с несколькими связями "один ко многим". Например, система ввода заказов будет содержать таблицы, соответствующие клиентам, заказам и позициям строк заказов. У конкретного клиента может быть несколько заказов, каждый из которых состоит из нескольких позиций. Такие данные могут быть представлены пользователю с помощью двух DropDownLists и GridView. Первый DropDownList будет иметь элемент списка для каждого клиента в базе данных, а второй — это заказы, размещенные выбранным клиентом. GridView выводит список строк из выбранного порядка.

Хотя база данных Northwind содержит канонические сведения о клиентах, заказах и заказах в таблицах Customers, Ordersи Order Details , эти таблицы не фиксируются в нашей архитектуре. Тем не менее, мы по-прежнему можем проиллюстрировать использование двух зависимых DropDownLists. В первом списке DropDownList будут перечислены категории, а во втором — продукты, относящиеся к выбранной категории. После этого в Представлении сведений отобразится список сведений о выбранном продукте.

Шаг 1. Создание и заполнение раскрывающегося списка категорий

Наша первая цель — добавить Раскрывающийся список, в который перечислены категории. Эти шаги были подробно рассмотрены в предыдущем руководстве, но приведены здесь для полноты.

Откройте страницу MasterDetailsDetails.aspx в папке Filtering , добавьте на нее раскрывающийся список, задайте для ее ID свойства Categoriesзначение , а затем щелкните ссылку Настройка источника данных в смарт-теге. В мастере настройки источника данных выберите добавление нового источника данных.

Добавление нового источника данных для раскрывающегося списка

Рис. 1. Добавление нового источника данных для Раскрывающегося списка (щелкните для просмотра полноразмерного изображения)

Новый источник данных, естественно, должен быть ObjectDataSource. Присвойте этому новому объекту имя ObjectDataSource CategoriesDataSource и вызовите CategoriesBLL метод объекта GetCategories() .

Выбор использования класса CategoriesBLL

Рис. 2. Выбор использования CategoriesBLL класса (щелкните для просмотра полноразмерного изображения)

Настройка ObjectDataSource для использования метода GetCategories()

Рис. 3. Настройка ObjectDataSource для использования GetCategories() метода (щелкните для просмотра полноразмерного изображения)

После настройки ObjectDataSource по-прежнему необходимо указать, какое поле источника данных должно отображаться в Categories Раскрывающемся списке, а какое — в качестве значения для элемента списка. CategoryName Задайте поле в качестве отображения и CategoryID значения для каждого элемента списка.

Отображение поля CategoryName в раскрывающемся списке и использование CategoryID в качестве значения

Рис. 4. Отображение поля в раскрывающемся CategoryName списке и использование CategoryID в качестве значения (щелкните для просмотра полноразмерного изображения)

На этом этапе у нас есть элемент управления DropDownList (Categories), заполненный записями из Categories таблицы. Когда пользователь выбирает новую категорию в Раскрывающемся списке, нам потребуется выполнить обратную передачу, чтобы обновить раскрывающийся список продукта, который мы создадим на шаге 2. Поэтому проверка параметр Включить AutoPostBack из categories смарт-тега DropDownList.

Включение AutoPostBack для раскрывающегося списка категорий

Рис. 5. Включение AutoPostBack для Categories Раскрывающегося списка (щелкните для просмотра полноразмерного изображения)

Шаг 2. Отображение продуктов выбранной категории во втором раскрывающемся списке

Categories После завершения dropDownList наш следующий шаг заключается в отображении раскрывающегося списка продуктов, относящихся к выбранной категории. Для этого добавьте еще один Раскрывающийся список на страницу с именем ProductsByCategory. Как и в случае Categories с DropDownList, создайте объект ObjectDataSource для ProductsByCategory DropDownList с именем ProductsByCategoryDataSource.

Добавление нового источника данных для раскрывающегося списка ProductsByCategory

Рис. 6. Добавление нового источника данных для раскрывающегося ProductsByCategory списка (щелкните для просмотра полноразмерного изображения)

Создание объекта ObjectDataSource с именем ProductsByCategoryDataSource

Рис. 7. Создание объекта ObjectDataSource с именем ProductsByCategoryDataSource (щелкните для просмотра полноразмерного изображения)

Так как dropDownList ProductsByCategory должен отображать только те продукты, которые относятся к выбранной категории, объект ObjectDataSource вызывает GetProductsByCategoryID(categoryID) метод из ProductsBLL объекта .

Снимок экрана: окно Настройка источника данных — productsByCategoryDataSource с раскрывающимся меню бизнес-объекта с выбранным элементом ProductsBLL и выделенной кнопкой Далее.

Рис. 8. Выбор использования ProductsBLL класса (щелкните для просмотра полноразмерного изображения)

Настройка ObjectDataSource для использования метода GetProductsByCategoryID(categoryID)

Рис. 9. Настройка ObjectDataSource для использования GetProductsByCategoryID(categoryID) метода (щелкните для просмотра полноразмерного изображения)

На последнем шаге мастера необходимо указать значение categoryID параметра . Назначьте этот параметр выбранному элементу из Раскрывающегося Categories списка.

Извлечение значения параметра categoryID из раскрывающегося списка категорий

Рис. 10. Извлечение categoryID значения параметра из Categories раскрывающегося списка (щелкните для просмотра полноразмерного изображения)

После настройки ObjectDataSource остается только указать, какие поля источника данных используются для отображения и значения элементов DropDownList. ProductName Отображение поля и использование поля в ProductID качестве значения.

Укажите поля источника данных, используемые для свойств текста и значения ListItems раскрывающегося списка

Рис. 11. Указание полей источника данных, используемых для свойств и Value свойств раскрывающегося списка TextListItem (щелкните для просмотра полноразмерного изображения)

При настройке ObjectDataSource и ProductsByCategory DropDownList на нашей странице будут отображаться два списка DropDownList: первый будет выводить список всех категорий, а второй — эти продукты, принадлежащие выбранной категории. Когда пользователь выбирает новую категорию из первого DropDownList, происходит обратная связь, а второй DropDownList будет восстановлен, отображая продукты, которые относятся к только что выбранной категории. Рисунки 12 и 13 показаны MasterDetailsDetails.aspx в действии при просмотре в браузере.

При первом посещении страницы выбирается категория

Рис. 12. При первом посещении страницы выбрана категория "Напитки" (щелкните для просмотра полноразмерного изображения)

Выбор другой категории Отображает продукты новой категории

Рис. 13. Выбор другой категории Отображает продукты новой категории (щелкните для просмотра полноразмерного изображения)

В настоящее productsByCategory время dropDownList при изменении не вызывает обратной передачи. Однако мы хотим, чтобы обратная связь произошла после добавления DetailsView для отображения сведений о выбранном продукте (шаг 3). Поэтому проверка флажок Включить AutoPostBack из productsByCategory смарт-тега DropDownList.

Включение функции AutoPostBack для раскрывающегося списка productsByCategory

Рис. 14. Включение функции AutoPostBack для productsByCategory раскрывающегося списка (щелкните для просмотра полноразмерного изображения)

Шаг 3. Использование DetailsView для отображения сведений о выбранном продукте

Последним шагом является отображение сведений о выбранном продукте в DetailsView. Для этого добавьте DetailsView на страницу, задайте для его ID свойства ProductDetailsзначение и создайте для него объект ObjectDataSource. Настройте этот объект ObjectDataSource для извлечения данных из ProductsBLL метода класса GetProductByProductID(productID) , используя выбранное ProductsByCategory значение DropDownList для значения productID параметра .

Снимок экрана: окно Настройка источника данных — productsByCategoryDataSource с открытым раскрывающимся меню бизнес-объекта. Выбран пункт ProductsBLL и выделена кнопка Далее.

Рис. 15. Выбор использования ProductsBLL класса (щелкните для просмотра полноразмерного изображения)

Настройка ObjectDataSource для использования метода GetProductByProductID(productID)

Рис. 16. Настройка ObjectDataSource для использования GetProductByProductID(productID) метода (щелкните для просмотра полноразмерного изображения)

Извлечение значения параметра productID из раскрывающегося списка ProductsByCategory

Рис. 17. Извлечение productID значения параметра из ProductsByCategory раскрывающегося списка (щелкните для просмотра полноразмерного изображения)

Вы можете выбрать отображение любого из доступных полей в Представлении сведений ProductDetails . Я решил удалить ProductIDполя , SupplierIDи CategoryID , а остальные поля переупорядочены и отформатированы. Кроме того, я очистил свойства DetailsView Height и Width , что позволило DetailsView расширить до ширины, необходимой для лучшего отображения данных, а не ограничивать их заданным размером. Полная разметка отображается ниже:

<asp:DetailsView ID="ProductDetails" runat="server"
    AutoGenerateRows="False" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" EnableViewState="False">
    <Fields>
        <asp:BoundField DataField="ProductName"
          HeaderText="Product" SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName"
          HeaderText="Category" ReadOnly="True"
          SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName"
          HeaderText="Supplier" ReadOnly="True"
          SortExpression="SupplierName" />
        <asp:BoundField DataField="QuantityPerUnit"
           HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice"
           DataFormatString="{0:c}" HeaderText="Price"
            HtmlEncode="False" SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock"
            HeaderText="UnitsInStock"
            SortExpression="Units In Stock" />
        <asp:BoundField DataField="UnitsOnOrder"
            HeaderText="UnitsOnOrder"
            SortExpression="Units On Order" />
        <asp:BoundField DataField="ReorderLevel"
            HeaderText="ReorderLevel" SortExpression="Reorder Level" />
        <asp:CheckBoxField DataField="Discontinued"
            HeaderText="Discontinued" SortExpression="Discontinued" />
    </Fields>
</asp:DetailsView>

Уделите немного времени, чтобы опробовать страницу MasterDetailsDetails.aspx в браузере. На первый взгляд может показаться, что все работает, как нужно, но есть тонкие проблемы. При выборе новой категории ProductsByCategory список DropDownList обновляется, чтобы включить эти продукты для выбранной категории, но ProductDetails в DetailsView по-прежнему отображаются предыдущие сведения о продукте. DetailsView обновляется при выборе другого продукта для выбранной категории. Кроме того, при достаточно тщательном тестировании вы обнаружите, что при постоянном выборе новых категорий (например, при выборе напитков в Categories Раскрывающемся списке, затем приправы, затем кондитерских изделий) все остальные категории ProductDetails приведут к обновлению DetailsView.

Чтобы упростить конкретизацию этой проблемы, рассмотрим конкретный пример. При первом посещении страницы выбирается категория «Напитки», а связанные продукты загружаются в ProductsByCategory Раскрывающийся список. Chai является выбранным продуктом, и его сведения отображаются в ProductDetails DetailsView, как показано на рисунке 18.

Сведения о выбранном продукте отображаются в представлении сведений

Рис. 18. Сведения о выбранном продукте отображаются в Представлении сведений (щелкните для просмотра полноразмерного изображения)

Если выбрать категорию с "Напитки" на "Приправы", происходит обратная связь и ProductsByCategory dropDownList обновляется соответствующим образом, но DetailsView по-прежнему отображает сведения о Chai.

Сведения о ранее выбранном продукте по-прежнему отображаются

Рис. 19. Сведения о ранее выбранном продукте по-прежнему отображаются (щелкните для просмотра полноразмерного изображения)

При выборе нового продукта из списка представление DetailsView обновляется должным образом. Если вы выберете новую категорию после изменения продукта, detailsView снова не будет обновляться. Однако если вместо выбора нового продукта вы выбрали новую категорию, представление DetailsView обновится. Что в мире происходит здесь?

Проблема связана со временем в жизненном цикле страницы. При каждом запросе страницы она выполняет ряд шагов в качестве отрисовки. На одном из этих шагов элементы управления ObjectDataSource проверка, чтобы узнать, изменилось ли какое-либо из их SelectParameters значений. Если это так, веб-элемент управления данными, привязанный к ObjectDataSource, знает, что ему необходимо обновить отображение. Например, при выборе новой категории ObjectDataSource обнаруживает, ProductsByCategoryDataSource что ее значения параметров изменились, и ProductsByCategory DropDownList повторно привязывает себя, получая продукты для выбранной категории.

Проблема, возникающая в этой ситуации, заключается в том, что точка жизненного цикла страницы, в которую ObjectDataSources проверка для измененных параметров, происходит до повторной привязки связанных веб-элементов управления данными. Таким образом, при выборе новой категории ProductsByCategoryDataSource ObjectDataSource обнаруживает изменение значения своего параметра. Объект ObjectDataSource, используемый ProductDetails DetailsView, не замечает таких изменений, так как ProductsByCategory DropDownList еще не был восстановлен. Позже в жизненном цикле ProductsByCategory DropDownList повторно привязывался к объекту ObjectDataSource, захватывая продукты для только что выбранной категории. ProductsByCategory Хотя значение DropDownList изменилось, ProductDetails объект ObjectDataSource DetailsView уже выполнил проверка значение параметра, поэтому DetailsView отображает предыдущие результаты. Это взаимодействие показано на рис. 20.

Значение Раскрывающегося списка ProductsByCategory изменяется после того, как Объект ObjectDataSource Объекта ProductDetails DetailsView проверяет наличие изменений.

Рис. 20. Значение ProductsByCategory Раскрывающегося списка изменяется после ProductDetails того, как ObjectDataSource Объекта DetailsView проверяет наличие изменений (щелкните, чтобы просмотреть полноразмерное изображение)

Чтобы устранить эту проблему, необходимо явно повторно привязать ProductDetails DetailsView после привязки ProductsByCategory DropDownList. Это можно сделать, вызвав ProductDetails метод DetailsView DataBind() при ProductsByCategory возникновении события DropDownList DataBound . Добавьте следующий код обработчика событий в MasterDetailsDetails.aspx класс кода программной части страницы (сведения о добавлении обработчика событий см. в разделе "Программное задание значений параметров ObjectDataSource").

Protected Sub ProductsByCategory_DataBound(sender As Object, e As EventArgs) _
    Handles ProductsByCategory.DataBound
        ProductDetails.DataBind()
End Sub

После добавления этого явного ProductDetails вызова метода DetailsView DataBind() учебник работает должным образом. На рисунке 21 показано, как это изменение устранило проблему, возникшую ранее.

Представление ProductDetails DetailsView явно обновляется при возникновении события DataBound списка ProductsByCategory DropDownList.

Рис. 21. DetailsView ProductDetails явно обновляется при ProductsByCategory возникновении события DropDownList DataBound (щелкните, чтобы просмотреть полноразмерное изображение)

Сводка

DropDownList служит идеальным элементом пользовательского интерфейса для master/подробных отчетов, в которых между master и подробными записями существует связь "один ко многим". В предыдущем руководстве мы узнали, как использовать один dropDownList для фильтрации продуктов, отображаемых по выбранной категории. В этом руководстве мы заменили GridView продуктов dropDownList и использовали DetailsView для отображения сведений о выбранном продукте. Основные понятия, описанные в этом руководстве, можно легко распространить на модели данных, включающие несколько связей "один ко многим", таких как клиенты, заказы и элементы заказов. Как правило, вы всегда можете добавить DropDownList для каждой из "одной" сущностей в отношениях "один ко многим".

Счастливое программирование!

Об авторе

Скотт Митчелл (Scott Mitchell), автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с Веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часа. Его можно связать по адресу mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.

Отдельная благодарность

Эта серия учебников была проверена многими полезными рецензентами. Ведущим рецензентом этого руководства был Хилтон Гисеноу. Хотите ознакомиться с моими предстоящими статьями MSDN? Если да, опустите мне строку в mitchell@4GuysFromRolla.com.