Отчет "Основной/подробности" с использованием маркированного списка основных записей с элементом управления DataList для подробных сведений (C#)Master/Detail Using a Bulleted List of Master Records with a Details DataList (C#)

по Скотт Митчеллby Scott Mitchell

Скачивание примера приложения или Загрузка PDF-файлаDownload Sample App or Download PDF

В этом учебнике мы будем сжимать отчет «основной/подробности» из предыдущего руководства на одну страницу, отображая маркированный список имен категорий в левой части экрана и продукты выбранной категории в правой части экрана.In this tutorial we'll compress the two-page master/detail report of the previous tutorial into a single page, showing a bulleted list of category names on the left side of the screen and the selected category's products on the right of the screen.

ВведениеIntroduction

В предыдущем учебном курсе мы рассматривали разделение отчета «основной/подробности» на две страницы.In the preceding tutorial we looked at how to separate a master/detail report across two pages. На главной странице мы использовали элемент управления Repeater для отображения маркированного списка категорий.In the master page we used a Repeater control to render a bulleted list of categories. Имя каждой категории было гиперссылкой, при щелчке которого пользователь перейдет на страницу сведений, где в DataList отображались продукты, принадлежащие выбранной категории.Each category name was a hyperlink that, when clicked, would take the user to the details page, where a two-column DataList showed those products belonging to the selected category.

В этом учебнике мы будем сжимать учебник с двумя страницами на одной странице, отображая маркированный список имен категорий в левой части экрана с именами категорий, отображаемыми как LinkButton.In this tutorial we'll compress the two-page tutorial into a single page, showing a bulleted list of category names on the left side of the screen with each category name rendered as a LinkButton. Щелкнув одну из категорий LinkButton, можно выполнить обратную передачу и привязать продукты выбранной категории к элементу DataList с двумя столбцами справа от экрана.Clicking one of the category name LinkButtons induces a postback and binds the selected category s products to a two-column DataList on the right of the screen. В дополнение к отображению каждого имени категории, в элементе Repeater слева отображается общее количество продуктов для данной категории (см. рис. 1).In addition to displaying each category s name, the Repeater on the left shows how many total products there are for a given category (see Figure 1).

имя категории s и общее количество продуктов отображаются слеваThe Category s Name and Total Number of Products are Displayed on the Left

Рис. 1. имя категории и общее количество продуктов отображаются слева (щелкните, чтобы просмотреть изображение с полным размером)Figure 1: The Category s Name and Total Number of Products are Displayed on the Left (Click to view full-size image)

Шаг 1. Отображение элемента Repeater в левой части экранаStep 1: Displaying a Repeater in the Left Portion of the Screen

Для работы с этим руководством необходимо, чтобы маркированный список категорий отображался слева от выбранных продуктов категории s.For this tutorial we need to have the bulleted list of categories appear to the left of the selected category s products. Содержимое на веб-странице может быть размещено с помощью стандартных тегов абзацев, неразрывных пробелов, <table> s и т. д. или с помощью методов каскадной таблицы стилей (CSS).Content within a web page can be positioned using standard HTML elements paragraph tags, non-breaking spaces, <table> s, and so on or through cascading stylesheet (CSS) techniques. Во всех наших руководствах мы использовали методы CSS для позиционирования.All of our tutorials thus far have used CSS techniques for positioning. При построении пользовательского интерфейса навигации на главной странице в учебном курсе главные страницы и Навигация по сайту было использовано абсолютное позиционирование, указывающее точное смещение пикселя для списка переходов и основное содержимое.When we built the navigation user interface in our master page in the Master Pages and Site Navigation tutorial we used absolute positioning, indicating the precise pixel offset for the navigation list and the main content. Кроме того, CSS можно использовать для размещения одного элемента справа или слева от другого до плавающего.Alternatively, CSS can be used to position one element to the right or left of another through floating. Можно отобразить маркированный список категорий слева от выбранных продуктов категории s, перечисляя элемент управления Repeater слева от элемента управления DataList.We can have the bulleted list of categories appear to the left of the selected category s products by floating the Repeater to the left of the DataList

Откройте страницу CategoriesAndProducts.aspx из папки DataListRepeaterFiltering и добавьте ее на страницу a Repeater и DataList.Open the CategoriesAndProducts.aspx page from the DataListRepeaterFiltering folder and add to the page a Repeater and a DataList. Задайте для параметра Repeater s ID значение Categories, а для элемента DataList s — значение CategoryProducts.Set the Repeater s ID to Categories and the DataList s to CategoryProducts. Перейдите в представление исходного кода и помещайте элементы управления Repeater и DataList в свои элементы <div>.Go to the Source view and put the Repeater and DataList controls within their own <div> elements. То есть сначала заключите элемент управления Repeater внутрь элемента <div>, а затем элемент управления DataList в своем собственном элементе <div> непосредственно после элемента управления Repeater.That is, enclose the Repeater within a <div> element first and then the DataList in its own <div> element directly after the Repeater. Разметка на этом этапе должна выглядеть следующим образом:Your markup at this point should look similar to the following:

<div>
    <asp:Repeater ID="Categories" runat="server">
    </asp:Repeater>
</div>
<div>
    <asp:DataList ID="CategoryProducts" runat="server">
    </asp:DataList>
</div>

Чтобы переместить элемент управления Repeater на левую часть DataList, необходимо использовать атрибут стиля CSS float, вот так:To float the Repeater to the left of the DataList, we need to use the float CSS style attribute, like so:

<div>
    Repeater
</div>
<div>
    DataList
</div>

float: left; перемещает первый элемент <div> влево от второго.The float: left; floats the first <div> element to the left of the second one. Параметры width и padding-right указывают первый <div> width и то, сколько дополнений добавляется между содержимым <div> элемента и его правым полем.The width and padding-right settings indicate the first <div> s width and how much padding is added between the <div> element s content and its right margin. Дополнительные сведения об плавающих элементах CSS см. в флоатуториал.For more information on floating elements in CSS check out the Floatutorial.

Вместо того, чтобы задавать стиль непосредственно через первый <p> элемент style атрибут, давайте создадим новый класс CSS в Styles.css с именем FloatLeft:Rather than specify the style setting directly through the first <p> element s style attribute, let s instead create a new CSS class in Styles.css named FloatLeft:

.FloatLeft
{
    float: left;
    width: 33%;
    padding-right: 10px;
}

Затем можно заменить <div> <div class="FloatLeft">.Then we can replace the <div> with <div class="FloatLeft">.

После добавления класса CSS и настройки разметки на странице CategoriesAndProducts.aspx перейдите в конструктор.After adding the CSS class and configuring the markup in the CategoriesAndProducts.aspx page, go to the Designer. Элемент управления Repeater должен быть плавающим слева от элемента управления DataList (хотя теперь он отображается серым цветом, так как мы еще не настроили их источники данных или шаблоны).You should see the Repeater floating to the left of the DataList (although right now both just appear as gray boxes since we ve yet to configure their data sources or templates).

элемент управления Repeater находится слева от DataListThe Repeater is Floated to the Left of the DataList

Рис. 2. элемент управления Repeater находится слева от элемента управления DataList (щелкните, чтобы просмотреть изображение с полным размером)Figure 2: The Repeater is Floated to the Left of the DataList (Click to view full-size image)

Шаг 2. Определение количества продуктов для каждой категорииStep 2: Determining the Number of Products for Each Category

После завершения разметки Repeater и DataList мы повторно готовы привязать данные категорий к элементу управления Repeater.With the Repeater and DataList s surrounding markup complete, we re ready to bind the category data to the Repeater control. Однако, как показано в маркированном списке категорий на рис. 1, в дополнение к именам категорий s также необходимо отобразить количество продуктов, связанных с категорией.However, as the bulleted list of categories in Figure 1 shows, in addition to each category s name we also need to display the number of products associated with the category. Чтобы получить доступ к этим сведениям, можно выполнить одно из следующих действий:To access this information we can either:

  • Определите эти сведения на основе класса кода программной части ASP.NET Page s.Determine this information from the ASP.NET page s code-behind class. Учитывая конкретную categoryID мы можем определить количество связанных продуктов, вызвав метод ProductsBLL класса s GetProductsByCategoryID(categoryID).Given a particular categoryID we can determine the number of associated products by calling the ProductsBLL class s GetProductsByCategoryID(categoryID) method. Этот метод возвращает объект ProductsDataTable, свойство Count которого показывает, сколько ProductsRow существует, что представляет собой количество продуктов для указанного categoryID .This method returns a ProductsDataTable object whose Count property indicates how many ProductsRow s exists, which is the number of products for the specified categoryID. Можно создать обработчик событий ItemDataBound для элемента Repeater, который для каждой категории, привязанной к элементу Repeater, вызывает метод ProductsBLL класса s GetProductsByCategoryID(categoryID) и включает его количество в выходных данных.We can create an ItemDataBound event handler for the Repeater that, for each category bound to the Repeater, calls the ProductsBLL class s GetProductsByCategoryID(categoryID) method and includes its count in the output.
  • Обновите CategoriesDataTable в типизированном наборе данных, чтобы включить столбец NumberOfProducts.Update the CategoriesDataTable in the Typed DataSet to include a NumberOfProducts column. Затем можно обновить метод GetCategories() в CategoriesDataTable, чтобы включить эти сведения, или же оставить GetCategories() "как есть" и создать новый метод CategoriesDataTable с именем GetCategoriesAndNumberOfProducts().We can then update the GetCategories() method in the CategoriesDataTable to include this information or, alternatively, leave GetCategories() as-is and create a new CategoriesDataTable method called GetCategoriesAndNumberOfProducts().

Давайте рассмотрим оба этих метода.Let s explore both of these techniques. Первый подход проще реализовать, так как нам не нужно обновлять уровень доступа к данным. Однако для этого требуется больше связи с базой данных.The first approach is simpler to implement since we don t need to update the Data Access Layer; however, it requires more communications with the database. Вызов метода ProductsBLL класса GetProductsByCategoryID(categoryID) в обработчике событий ItemDataBound добавляет дополнительный вызов базы данных для каждой категории, отображаемой в элементе Repeater.The call to the ProductsBLL class s GetProductsByCategoryID(categoryID) method in the ItemDataBound event handler adds an extra database call for each category displayed in the Repeater. При использовании этого метода существует n + 1 вызовов баз данных, где n — число категорий, отображаемых в элементе Repeater.With this technique there are N + 1 database calls, where N is the number of categories displayed in the Repeater. При втором подходе возвращается число продуктов со сведениями о каждой категории из метода CategoriesBLL класса s GetCategories() (или GetCategoriesAndNumberOfProducts()), что приводит к простому обращению к базе данных.With the second approach, the product count is returned with information about each category from the CategoriesBLL class s GetCategories() (or GetCategoriesAndNumberOfProducts()) method, thereby resulting in just one trip to the database.

Определение количества продуктов в обработчике событий ItemDataBoundDetermining the Number of Products in the ItemDataBound Event Handler

Для определения количества продуктов для каждой категории в обработчике событий Repeater ItemDataBound не требуется вносить изменения в имеющийся уровень доступа к данным.Determining the number of products for each category in the Repeater s ItemDataBound event handler does not require any modifications to our existing Data Access Layer. Все изменения можно вносить непосредственно на странице CategoriesAndProducts.aspx.All modifications can be made directly within the CategoriesAndProducts.aspx page. Начните с добавления нового элемента управления ObjectDataSource с именем CategoriesDataSource через смарт-тег Repeater s.Start by adding a new ObjectDataSource named CategoriesDataSource via the Repeater s smart tag. Затем настройте CategoriesDataSource ObjectDataSource таким образом, чтобы он получал свои данные из метода CategoriesBLL класса s GetCategories().Next, configure the CategoriesDataSource ObjectDataSource so that it retrieves its data from the CategoriesBLL class s GetCategories() method.

настроить ObjectDataSource для использования метода класса CategoriesBLL-Categories ()Configure the ObjectDataSource to Use the CategoriesBLL class s GetCategories() Method

Рис. 3. Настройка ObjectDataSource для использования метода GetCategories() CategoriesBLL классов (щелкните, чтобы просмотреть изображение с полным размером)Figure 3: Configure the ObjectDataSource to Use the CategoriesBLL class s GetCategories() Method (Click to view full-size image)

Каждый элемент в Categories Repeater должен быть щелчком мыши, и при нажатии CategoryProducts DataList отображает эти продукты для выбранной категории.Each item in the Categories Repeater needs to be clickable and, when clicked, cause the CategoryProducts DataList to display those products for the selected category. Это можно сделать, сделав каждую категорию гиперссылкой, связав ее с той же страницей (CategoriesAndProducts.aspx), но передавая CategoryID через строку запроса, как показано в предыдущем руководстве.This can be accomplished by making each category a hyperlink, linking back to this same page (CategoriesAndProducts.aspx), but passing the CategoryID through the querystring, much like we saw in the previous tutorial. Преимуществом этого подхода является то, что страница, отображающая определенные продукты категории, может быть помечена и проиндексирована поисковой системой.The advantage of this approach is that a page displaying a particular category s products can be bookmarked and indexed by a search engine.

Кроме того, можно создать каждую категорию с помощью элемента LinkButton, который мы будем использовать для работы с этим руководством.Alternatively, we can make each category a LinkButton, which is the approach we'll use for this tutorial. Элемент LinkButton отображается в браузере User s как гиперссылка, но при щелчке вызывает обратную передачу; при обратной передаче необходимо обновить элемент управления DataList, чтобы отобразить продукты, принадлежащие выбранной категории.The LinkButton renders in the user s browser as a hyperlink but, when clicked, induces a postback; on postback, the DataList s ObjectDataSource needs to be refreshed to display those products belonging to the selected category. В этом учебнике использование гиперссылки имеет больше смысла, чем использование элемента LinkButton; Однако могут существовать и другие сценарии, в которых использование элемента LinkButton является более выгодным.For this tutorial, using a hyperlink makes more sense than using a LinkButton; however, there may be other scenarios where using a LinkButton is more advantageous. Хотя подход гиперссылки идеально подходит для этого примера, давайте рассмотрим использование LinkButton.While the hyperlink approach would be ideal for this example, let s instead explore using the LinkButton. Как мы увидим, использование LinkButton вводит некоторые проблемы, которые в противном случае не будут возникать с гиперссылкой.As we'll see, using a LinkButton introduces some challenges that would not otherwise arise with a hyperlink. Поэтому использование элемента LinkButton в этом учебнике выделит эти проблемы и предоставит решения для тех сценариев, в которых вместо гиперссылки может потребоваться использовать элемент LinkButton.Therefore, using a LinkButton in this tutorial will highlight these challenges and help provide solutions for those scenarios where we may want to use a LinkButton instead of a hyperlink.

Note

Рекомендуется повторить этот учебник, используя элемент управления HyperLink или элемент <a> вместо элемента LinkButton.You are encouraged to repeat this tutorial using a HyperLink control or <a> element in lieu of the LinkButton.

В следующей разметке показан декларативный синтаксис для элемента управления Repeater и ObjectDataSource.The following markup shows the declarative syntax for the Repeater and the ObjectDataSource. Обратите внимание, что шаблоны Repeater дорабатывают маркированный список с каждым элементом как LinkButton:Note that the Repeater s templates render a bulleted list with each item as a LinkButton:

<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><asp:LinkButton runat="server" ID="ViewCategory" /></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Note

В этом учебнике для элемента Repeater должно быть включено состояние просмотра (Обратите внимание на упущение EnableViewState="False" из декларативного синтаксиса Repeater).For this tutorial the Repeater must have its view state enabled (note the omission of the EnableViewState="False" from the Repeater s declarative syntax). На этапе 3 мы создадим обработчик событий для события Repeater ItemCommand, в котором мы будем обновлять коллекцию коллекций ObjectDataSource s SelectParameters.In step 3 we'll be creating an event handler for the Repeater s ItemCommand event in which we'll be updating the DataList s ObjectDataSource s SelectParameters collection. Однако ItemCommandRepeater не будет срабатывать, если состояние представления отключено.The Repeater s ItemCommand, however, won't fire if view state is disabled. Дополнительные сведения о том, почему должно быть включено состояние просмотра ItemCommand для запуска события Repeater, см. в разделе головоломка ASP.NET вопроса и его решения .See A Stumper of an ASP.NET Question and its solution for more information on why view state must be enabled for a Repeater s ItemCommand event to fire.

Свойство LinkButton со значением свойства ID ViewCategory не имеет установленного свойства Text.The LinkButton with the ID property value of ViewCategory does not have its Text property set. Если бы было просто отображалось имя категории, свойство Text было бы задано декларативно с помощью синтаксиса DataBinding следующим образом:If we had just wanted to display the category name, we would have set the Text property declaratively, through databinding syntax, like so:

<asp:LinkButton runat="server" ID="ViewCategory"
    Text='<%# Eval("CategoryName") %>' />

Однако мы хотим отобразить как имя категории, так и число продуктов, принадлежащих этой категории.However, we want to show both the category s name and the number of products belonging to that category. Эти сведения можно получить из обработчика событий Repeater ItemDataBound, сделав вызов метода ProductBLL класса s GetCategoriesByProductID(categoryID) и определив, сколько записей возвращается в результате ProductsDataTable, как показано в следующем коде:This information can be retrieved from the Repeater s ItemDataBound event handler by making a call to the ProductBLL class s GetCategoriesByProductID(categoryID) method and determining how many records are returned in the resulting ProductsDataTable, as the following code illustrates:

protected void Categories_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    // Make sure we're working with a data item...
    if (e.Item.ItemType == ListItemType.Item ||
        e.Item.ItemType == ListItemType.AlternatingItem)
    {
        // Reference the CategoriesRow instance bound to this RepeaterItem
        Northwind.CategoriesRow category =
            (Northwind.CategoriesRow) ((System.Data.DataRowView) e.Item.DataItem).Row;
        // Determine how many products are in this category
        NorthwindTableAdapters.ProductsTableAdapter productsAPI =
            new NorthwindTableAdapters.ProductsTableAdapter();
        int productCount =
            productsAPI.GetProductsByCategoryID(category.CategoryID).Count;
        // Reference the ViewCategory LinkButton and set its Text property
        LinkButton ViewCategory = (LinkButton)e.Item.FindControl("ViewCategory");
        ViewCategory.Text =
            string.Format("{0} ({1:N0})", category.CategoryName, productCount);
    }
}

Начнем с того, что мы работаем над тем, чтобы мы переработали элемент данных (один из которых ItemType Item или AlternatingItem), а затем сослаться на экземпляр CategoriesRow, который только что был привязан к текущему RepeaterItem.We start out by ensuring that we re working with a data item (one whose ItemType is Item or AlternatingItem) and then reference the CategoriesRow instance that has just been bound to the current RepeaterItem. Далее мы определяем количество продуктов для этой категории путем создания экземпляра класса ProductsBLL, вызова его метода GetCategoriesByProductID(categoryID) и определения количества записей, возвращаемых с помощью свойства Count.Next, we determine the number of products for this category by creating an instance of the ProductsBLL class, calling its GetCategoriesByProductID(categoryID) method, and determining the number of records returned using the Count property. Наконец, ViewCategory LinkButton в ItemTemplate является ссылкой, а свойство Text имеет значение CategoryName (нумберофпродуктсинкатегори), где нумберофпродуктсинкатегори отформатировано как число с нулевыми десятичными разрядами.Finally, the ViewCategory LinkButton in the ItemTemplate is references and its Text property is set to CategoryName (NumberOfProductsInCategory), where NumberOfProductsInCategory is formatted as a number with zero decimal places.

Note

Кроме того, мы могли бы добавить функцию форматирования в класс кода программной части ASP.NET Page s, который принимает категории s CategoryName и CategoryID значения и возвращает CategoryName сцепление с числом продуктов в категории (как определено вызовом метода GetCategoriesByProductID(categoryID)).Alternatively, we could have added a formatting function to the ASP.NET page s code-behind class that accepts a category s CategoryName and CategoryID values and returns the CategoryName concatenated with the number of products in the category (as determined by calling the GetCategoriesByProductID(categoryID) method). Результаты такой функции форматирования можно декларативно назначить свойству "текст" в LinkButton, заменив потребность в обработчике событий ItemDataBound.The results of such a formatting function could be declaratively assigned to the LinkButton s Text property replacing the need for the ItemDataBound event handler. Дополнительные сведения об использовании функций форматирования см. в разделе Использование полей TemplateField в элементе управления GridView или при форматировании элементов DataList и Repeater на основе учебников по работе с данными.Refer to the Using TemplateFields in the GridView Control or Formatting the DataList and Repeater Based Upon Data tutorials for more information on using formatting functions.

После добавления этого обработчика событий протестируйте страницу в браузере.After adding this event handler, take a moment to test the page through a browser. Обратите внимание, что каждая категория указана в маркированном списке, отображая название категории s и число продуктов, связанных с категорией (см. рис. 4).Note how each category is listed in a bulleted list, displaying the category s name and the number of products associated with the category (see Figure 4).

отображаются имена и количество продуктов категории sEach Category s Name and Number of Products are Displayed

Рис. 4. отображаются имена и количество продуктов каждой категории (щелкните, чтобы просмотреть изображение с полным размером).Figure 4: Each Category s Name and Number of Products are Displayed (Click to view full-size image)

ОбновлениеCategoriesDataTableиCategoriesTableAdapterдля включения числа продуктов для каждой категорииUpdating theCategoriesDataTableandCategoriesTableAdapterto Include the Number of Products for Each Category

Вместо того чтобы определять количество продуктов для каждой категории в качестве привязки к Repeater, мы можем упростить этот процесс, настроив CategoriesDataTable и CategoriesTableAdapter на уровне доступа к данным, чтобы включить эти сведения в исходный код.Rather than determining the number of products for each category as it s bound to the Repeater, we can streamline this process by adjusting the CategoriesDataTable and CategoriesTableAdapter in the Data Access Layer to include this information natively. Для этого необходимо добавить новый столбец для CategoriesDataTable для хранения количества связанных продуктов.To achieve this, we must add a new column to CategoriesDataTable to hold the number of associated products. Чтобы добавить новый столбец к DataTable, откройте типизированный набор данных (App_Code\DAL\Northwind.xsd), щелкните правой кнопкой мыши таблицу данных, которую нужно изменить, и выберите Добавить/столбец.To add a new column to a DataTable, open the Typed DataSet (App_Code\DAL\Northwind.xsd), right-click on the DataTable to modify, and choose Add / Column. Добавьте новый столбец в CategoriesDataTable (см. рис. 5).Add a new column to the CategoriesDataTable (see Figure 5).

добавить новый столбец в CategoriesDataSourceAdd a New Column to the CategoriesDataSource

Рис. 5. Добавление нового столбца в CategoriesDataSource (щелкните, чтобы просмотреть изображение с полным размером)Figure 5: Add a New Column to the CategoriesDataSource (Click to view full-size image)

При этом будет добавлен новый столбец с именем Column1, который можно изменить, просто введя другое имя.This will add a new column named Column1, which you can change by simply typing in a different name. Переименуйте этот новый столбец в NumberOfProducts.Rename this new column to NumberOfProducts. Далее необходимо настроить свойства этого столбца.Next, we need to configure this column s properties. Щелкните новый столбец и перейдите к окно свойств.Click on the new column and go to the Properties window. Измените свойство DataType столбцов с System.String на System.Int32 и задайте для свойства ReadOnly значение True, как показано на рис. 6.Change the column s DataType property from System.String to System.Int32 and set the ReadOnly property to True, as shown in Figure 6.

Установка свойств DataType и ReadOnly нового столбца

Рис. 6. задание свойств DataType и ReadOnly нового столбцаFigure 6: Set the DataType and ReadOnly Properties of the New Column

Хотя CategoriesDataTable теперь содержит столбец NumberOfProducts, его значение не задается ни одним из запросов TableAdapter s.While the CategoriesDataTable now has a NumberOfProducts column, its value is not set by any of the corresponding TableAdapter s queries. Мы можем обновить метод GetCategories(), чтобы получить эти сведения, если требуется, чтобы эти сведения возвращались при каждой получении сведений о категории.We can update the GetCategories() method to return this information if we want such information returned every time category information is retrieved. Однако в редких случаях нам нужно только извлечь количество связанных продуктов для категорий (например, просто для этого руководства), а затем оставить GetCategories() "как есть" и создать новый метод, возвращающий эту информацию.If, however, we only need to grab the number of associated products for the categories in rare instances (such as just for this tutorial), then we can leave GetCategories() as-is and create a new method that returns this information. Давайте будем использовать этот подход, создавая новый метод с именем GetCategoriesAndNumberOfProducts().Let s use this latter approach, creating a new method named GetCategoriesAndNumberOfProducts().

Чтобы добавить этот новый метод GetCategoriesAndNumberOfProducts(), щелкните правой кнопкой мыши CategoriesTableAdapter и выберите Создать запрос.To add this new GetCategoriesAndNumberOfProducts() method, right-click on the CategoriesTableAdapter and choose New Query. Откроется мастер настройки запросов адаптера таблицы TableAdapter, который мы использовали несколько раз в предыдущих руководствах.This brings up the TableAdapter Query Configuration Wizard, which we ve used numerous times in previous tutorials. Для этого метода запустите мастер, указав, что запрос использует специальный оператор SQL, возвращающий строки.For this method, start the wizard by indicating that the query uses an ad-hoc SQL statement that returns rows.

создать метод с помощью специального оператора SQLCreate the Method Using an Ad-Hoc SQL Statement

Рис. 7. Создание метода с помощью специального оператора SQL (щелкните, чтобы просмотреть изображение с полным размером)Figure 7: Create the Method Using an Ad-Hoc SQL Statement (Click to view full-size image)

инструкция SQL возвращает строкиThe SQL Statement Returns Rows

Рис. 8. Инструкция SQL возвращает строки (щелкните, чтобы просмотреть изображение с полным размером)Figure 8: The SQL Statement Returns Rows (Click to view full-size image)

На следующем экране мастера запрашивается запрос на использование.The next wizard screen prompts us for the query to use. Чтобы вернуть каждую категорию CategoryID, CategoryNameи Description полей, а также число продуктов, связанных с категорией, используйте следующую инструкцию SELECT:To return each category s CategoryID, CategoryName, and Description fields, along with the number of products associated with the category, use the following SELECT statement:

SELECT CategoryID, CategoryName, Description,
       (SELECT COUNT(*) FROM Products p WHERE p.CategoryID = c.CategoryID)
            as NumberOfProducts
FROM Categories c

указать используемый запросSpecify the Query to Use

Рис. 9. Указание используемого запроса (щелкните, чтобы просмотреть изображение с полным размером)Figure 9: Specify the Query to Use (Click to view full-size image)

Обратите внимание, что вложенный запрос, вычисляющий количество продуктов, связанных с категорией, имеет псевдоним в виде NumberOfProducts.Note that the subquery that computes the number of products associated with the category is aliased as NumberOfProducts. Такое сопоставление имен приводит к тому, что значение, возвращаемое этим вложенным запросом, связывается со столбцом CategoriesDataTable s NumberOfProducts.This naming match causes the value returned by this subquery to be associated with the CategoriesDataTable s NumberOfProducts column.

После ввода этого запроса последним шагом является выбор имени для нового метода.After entering this query, the last step is to choose the name for the new method. Используйте FillWithNumberOfProducts и GetCategoriesAndNumberOfProducts для заполнения DataTable и возврата шаблонов DataTable соответственно.Use FillWithNumberOfProducts and GetCategoriesAndNumberOfProducts for the Fill a DataTable and Return a DataTable patterns, respectively.

назовите новые методы TableAdapter s Филлвиснумберофпродуктс и GetCategoriesAndNumberOfProducts.Name the New TableAdapter s Methods FillWithNumberOfProducts and GetCategoriesAndNumberOfProducts

Рис. 10. именование новых методов адаптера таблицы FillWithNumberOfProducts и GetCategoriesAndNumberOfProducts (щелкните, чтобы просмотреть изображение с полным размером)Figure 10: Name the New TableAdapter s Methods FillWithNumberOfProducts and GetCategoriesAndNumberOfProducts (Click to view full-size image)

На этом этапе уровень доступа к данным был расширен и включает число продуктов для каждой категории.At this point the Data Access Layer has been extended to include the number of products per category. Так как весь уровень представления направляет все вызовы DAL через отдельный уровень бизнес-логики, необходимо добавить соответствующий метод GetCategoriesAndNumberOfProducts в класс CategoriesBLL:Since all our presentation layer routes all calls to the DAL through a separate Business Logic Layer we need to add a corresponding GetCategoriesAndNumberOfProducts method to the CategoriesBLL class:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.CategoriesDataTable GetCategoriesAndNumberOfProducts()
{
    return Adapter.GetCategoriesAndNumberOfProducts();
}

После выполнения DAL и BLL мы повторно готовы привязать эти данные к Categories Repeater в CategoriesAndProducts.aspx!With the DAL and BLL complete, we re ready to bind this data to the Categories Repeater in CategoriesAndProducts.aspx! Если вы уже создали элемент управления ObjectDataSource для элемента управления Repeater от определения числа продуктов в разделе обработчика событий ItemDataBound, удалите этот элемент ObjectDataSource и удалите параметр свойства Repeater s DataSourceID. Кроме того, не следует развязать событие Repeater ItemDataBound из обработчика событий, удалив синтаксис Handles Categories.OnItemDataBound в классе кода программной части ASP.NET.If you ve already created an ObjectDataSource for the Repeater from the Determining the Number of Products in the ItemDataBound Event Handler section, delete this ObjectDataSource and remove the Repeater s DataSourceID property setting; also unwire the Repeater s ItemDataBound event from the event handler by removing the Handles Categories.OnItemDataBound syntax in the ASP.NET code-behind class.

После возврата к исходному состоянию Repeater добавьте новый объект ObjectDataSource с именем CategoriesDataSource через смарт-тег Repeater s.With the Repeater back in its original state, add a new ObjectDataSource named CategoriesDataSource via the Repeater s smart tag. Настройте ObjectDataSource для использования класса CategoriesBLL, но вместо того, чтобы он использовал метод GetCategories(), он должен использовать GetCategoriesAndNumberOfProducts() вместо него (см. рис. 11).Configure the ObjectDataSource to use the CategoriesBLL class, but instead of having it use the GetCategories() method, have it use GetCategoriesAndNumberOfProducts() instead (see Figure 11).

настроить ObjectDataSource для использования метода GetCategoriesAndNumberOfProductsConfigure the ObjectDataSource to Use the GetCategoriesAndNumberOfProducts Method

Рис. 11. Настройка ObjectDataSource для использования метода GetCategoriesAndNumberOfProducts (щелкните, чтобы просмотреть изображение с полным размером)Figure 11: Configure the ObjectDataSource to Use the GetCategoriesAndNumberOfProducts Method (Click to view full-size image)

Затем обновите ItemTemplate таким образом, чтобы свойство LinkButton Text было декларативно назначено с помощью синтаксиса DataBinding и включало поля данных CategoryName и NumberOfProducts.Next, update the ItemTemplate so that the LinkButton s Text property is declaratively assigned using databinding syntax and includes both the CategoryName and NumberOfProducts data fields. Полная декларативная разметка для элемента управления Repeater и CategoriesDataSource ObjectDataSource соблюдается:The complete declarative markup for the Repeater and the CategoriesDataSource ObjectDataSource follow:

<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><asp:LinkButton runat="server" ID="ViewCategory"
                Text='<%# String.Format("{0} ({1:N0})", _
                    Eval("CategoryName"), Eval("NumberOfProducts")) %>' />
        </li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategoriesAndNumberOfProducts" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Выходные данные, отображаемые путем обновления DAL для включения NumberOfProducts столбца, аналогичны тому, как используется ItemDataBoundный метод обработчика событий (см. рис. 4 для просмотра снимка экрана с именами категорий и количеством продуктов).The output rendered by updating the DAL to include a NumberOfProducts column is the same as using the ItemDataBound event handler approach (refer back to Figure 4 to see a screen shot of the Repeater showing the category names and number of products).

Шаг 3. Отображение выбранных продуктов категорииStep 3: Displaying the Selected Category s Products

На этом этапе у нас есть Categories Repeater, отображающий список категорий вместе с количеством продуктов в каждой категории.At this point we have the Categories Repeater displaying the list of categories along with the number of products in each category. Элемент управления Repeater использует элемент управления LinkButton для каждой категории, которая при нажатии вызывает обратную передачу, после чего необходимо отобразить эти продукты для выбранной категории в CategoryProducts DataList.The Repeater uses a LinkButton for each category that, when clicked, causes a postback, at which point we need to display those products for the selected category in the CategoryProducts DataList.

Одна из проблем заключается в том, как разместить в DataList только те продукты для выбранной категории.One challenge facing us is how to have the DataList display just those products for the selected category. В базе данных " основной/подробности" с помощью выбираемого главного элемента управления GridView с руководством по DetailsView мы увидели, как создать элемент управления GridView, строки которого могли быть выбраны, а сведения о выбранных строках отображаются в элементе DetailsView на той же странице.In the Master/Detail Using a Selectable Master GridView with a Details DetailsView tutorial we saw how to build a GridView whose rows could be selected, with the selected row s details being displayed in a DetailsView on the same page. Элемент управления GridView возвращает сведения обо всех продуктах, используя метод ProductsBLL s GetProducts(), а элемент ObjectDataSource DetailsView извлекает сведения о выбранном продукте с помощью метода GetProductsByProductID(productID).The GridView s ObjectDataSource returned information about all products using the ProductsBLL s GetProducts() method while the DetailsView s ObjectDataSource retrieved information about the selected product using the GetProductsByProductID(productID) method. Значение параметра productID было предоставлено декларативно путем связывания его со значением свойства GridView s SelectedValue.The productID parameter value was provided declaratively by associating it with the value of the GridView s SelectedValue property. К сожалению, у элемента Repeater нет свойства SelectedValue и он не может выступать в качестве источника параметра.Unfortunately, the Repeater does not have a SelectedValue property and cannot serve as a parameter source.

Note

Это одна из проблем, которые появляются при использовании LinkButton в элементе Repeater.This is one of those challenges that appear when using the LinkButton in a Repeater. Если бы мы использовали гиперссылку для передачи CategoryID через строку запроса, мы могли бы использовать это поле QueryString в качестве источника для значения параметра s.Had we used a hyperlink to pass in the CategoryID through the querystring instead, we could use that QueryString field as the source for the parameter s value.

Прежде чем беспокоиться о недостатке свойства SelectedValue для элемента управления Repeater, позвольте s сначала привязать элемент управления DataList к элементу ObjectDataSource и указать его ItemTemplate.Before we worry about the lack of a SelectedValue property for the Repeater, though, let s first bind the DataList to an ObjectDataSource and specify its ItemTemplate.

В смарт-теге DataList добавьте новый элемент ObjectDataSource с именем CategoryProductsDataSource и настройте его для использования метода ProductsBLL класса s GetProductsByCategoryID(categoryID).From the DataList s smart tag, opt to add a new ObjectDataSource named CategoryProductsDataSource and configure it to use the ProductsBLL class s GetProductsByCategoryID(categoryID) method. Поскольку элемент управления DataList в этом учебнике предоставляет интерфейс только для чтения, вы можете установить в раскрывающихся списках на вкладках Вставка, обновление и удалить значение (нет).Since the DataList in this tutorial offers a read-only interface, feel free to set the drop-down lists in the INSERT, UPDATE, and DELETE tabs to (None).

настроить ObjectDataSource для использования метода GetProductsByCategoryID класса ProductsBLL (КодТипа)Configure the ObjectDataSource to Use ProductsBLL Class s GetProductsByCategoryID(categoryID) Method

Рис. 12. Настройка ObjectDataSource для использования метода ProductsBLL Class s GetProductsByCategoryID(categoryID) (щелкните, чтобы просмотреть изображение с полным размером)Figure 12: Configure the ObjectDataSource to Use ProductsBLL Class s GetProductsByCategoryID(categoryID) Method (Click to view full-size image)

Так как метод GetProductsByCategoryID(categoryID) принимает входной параметр ( categoryID ), мастер настройки источника данных позволяет указать источник параметра s.Since the GetProductsByCategoryID(categoryID) method expects an input parameter (categoryID), the Configure Data Source wizard allows us to specify the parameter s source. Если категории были перечислены в элементе управления GridView или DataList, мы устанавливаем раскрывающийся список источника параметров для управления, а параметр ControlID — ID веб-элемента.Had the categories been listed in a GridView or a DataList, we d set the Parameter source drop-down list to Control and the ControlID to the ID of the data Web control. Однако поскольку у элемента Repeater отсутствует свойство SelectedValue его нельзя использовать в качестве источника параметра.However, since the Repeater lacks a SelectedValue property it cannot be used as a parameter source. Если флажок установлен, то раскрывающийся список ControlID будет содержать только один элемент управления ID``CategoryProducts, ID DataList.If you check, you'll find that the ControlID drop-down list only contains one control ID``CategoryProducts, the ID of the DataList.

Пока установите в раскрывающемся списке Источник параметра значение нет.For now, set the Parameter source drop-down list to None. В итоге мы будем программно назначать значение этого параметра при щелчке элемента LinkButton в элементе Repeater.We'll end up programmatically assigning this parameter value when a category LinkButton is clicked in the Repeater.

не указывать источник параметра для параметра categoryIDDo Not Specify a Parameter Source for the categoryID Parameter

Рис. 13. не указывайте источник параметра для параметра categoryID (щелкните, чтобы просмотреть изображение с полным размером)Figure 13: Do Not Specify a Parameter Source for the categoryID Parameter (Click to view full-size image)

После завершения работы мастера настройки источника данных Visual Studio создает ItemTemplateDataList.After completing the Configure Data Source wizard, Visual Studio auto-generates the DataList s ItemTemplate. Замените этот ItemTemplate по умолчанию шаблоном, который использовался в предыдущем руководстве. также задайте для свойства RepeatColumns DataList s значение 2.Replace this default ItemTemplate with the template we used in the preceding tutorial; also, set the DataList s RepeatColumns property to 2. После внесения этих изменений декларативная разметка для элемента управления DataList и связанный с ней объект ObjectDataSource должны выглядеть следующим образом:After making these changes the declarative markup for your DataList and its associated ObjectDataSource should look like the following:

<asp:DataList ID="CategoryProducts" runat="server" DataKeyField="ProductID"
    DataSourceID="CategoryProductsDataSource" RepeatColumns="2"
    EnableViewState="False">
    <ItemTemplate>
        <h5><%# Eval("ProductName") %></h5>
        <p>
            Supplied by <%# Eval("SupplierName") %><br />
            <%# Eval("UnitPrice", "{0:C}") %>
        </p>
    </ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="CategoryProductsDataSource"
    OldValuesParameterFormatString="original_{0}"  runat="server"
    SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
    <SelectParameters>
        <asp:Parameter Name="categoryID" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

В настоящее время параметр categoryID CategoryProductsDataSource ObjectDataSource s не задан, поэтому при просмотре страницы продукты не отображаются.Currently, the CategoryProductsDataSource ObjectDataSource s categoryID parameter is never set, so no products are displayed when viewing the page. Для этого нужно задать значение этого параметра на основе CategoryID категории, нажатой в элементе Repeater.What we need to do is have this parameter value set based on the CategoryID of the clicked category in the Repeater. Это приводит к двум задачам: сначала, как определить, когда была нажата кнопка LinkButton в элементе Repeater s ItemTemplate; и во вторых, как можно определить CategoryID соответствующей категории, которую щелкнули LinkButton?This introduces two challenges: first, how do we determine when a LinkButton in the Repeater s ItemTemplate has been clicked; and second, how can we determine the CategoryID of the corresponding category whose LinkButton was clicked?

Элемент управления LinkButton, такой как Button и ImageButton, имеет событие Click и событиеCommand.The LinkButton like the Button and ImageButton controls has a Click event and a Command event. Событие Click предназначено для того, чтобы просто отметить, что была нажата кнопка LinkButton.The Click event is designed to simply note that the LinkButton has been clicked. Однако иногда в дополнение к тому, что была нажата кнопка LinkButton, нам также нужно передать некоторую дополнительную информацию в обработчик событий.At times, however, in addition to noting that the LinkButton has been clicked we also need to pass some extra information to the event handler. В этом случае эти дополнительные сведения могут быть назначены свойствам LinkButton CommandName и CommandArgument .If this is the case, the LinkButton s CommandName and CommandArgument properties can be assigned this extra information. Затем при нажатии элемента LinkButton срабатывает ее Command событие (вместо события Click), и обработчику событий передаются значения свойств CommandName и CommandArgument.Then, when the LinkButton is clicked, its Command event fires (instead of its Click event) and the event handler is passed the values of the CommandName and CommandArgument properties.

При возникновении события Command в шаблоне в элементе Repeater возникает событие repeaterItemCommand , и ему передается CommandName и CommandArgument значения нажатия элемента LinkButton (или кнопки или ImageButton).When a Command event is raised from within a template in the Repeater, the Repeater s ItemCommand event fires and is passed the CommandName and CommandArgument values of the clicked LinkButton (or Button or ImageButton). Таким образом, чтобы определить, когда была нажата Категория LinkButton в элементе Repeater, необходимо выполнить следующие действия.Therefore, to determine when a category LinkButton in the Repeater has been clicked, we need to do the following:

  1. Задайте для свойства CommandName элемента LinkButton в элементе Repeater ItemTemplate какое-то значение (я использовал Листпродуктс).Set the CommandName property of the LinkButton in the Repeater s ItemTemplate to some value (I ve used ListProducts ). Если задать это CommandName, то при нажатии кнопки LinkButton возникает событие Command LinkButton.By setting this CommandName value, the LinkButton s Command event fires when the LinkButton is clicked.
  2. Присвойте свойству CommandArgument LinkButton s значение текущего элемента CategoryID.Set the LinkButton s CommandArgument property to the value of the current item s CategoryID.
  3. Создайте обработчик событий для события Repeater ItemCommand.Create an event handler for the Repeater s ItemCommand event. В обработчике событий задайте для параметра CategoryProductsDataSource ObjectDataSource s CategoryID значение переданного CommandArgument.In the event handler, set the CategoryProductsDataSource ObjectDataSource s CategoryID parameter to the value of the passed-in CommandArgument.

Следующая ItemTemplate разметка для повторяющихся категорий, реализует шаги 1 и 2.The following ItemTemplate markup for the Categories Repeater implements steps 1 and 2. Обратите внимание, что CommandArgument значение присваивается элементам данных CategoryID с помощью синтаксиса DataBinding:Note how the CommandArgument value is assigned the data item s CategoryID using databinding syntax:

<ItemTemplate>
    <li>
        <asp:LinkButton CommandName="ListProducts"  runat="server"
            CommandArgument='<%# Eval("CategoryID") %>' ID="ViewCategory"
            Text='<%# string.Format("{0} ({1:N0})", _
                Eval("CategoryName"), Eval("NumberOfProducts")) %>'>
        </asp:LinkButton>
    </li>
</ItemTemplate>

При создании обработчика ItemCommand событий рекомендуется всегда сначала проверять значение входящего CommandName, так как любое событие Command, вызванное любой кнопкой, LinkButton или ImageButton в элементе Repeater, приведет к срабатыванию события ItemCommand.Whenever creating an ItemCommand event handler it is prudent to always first check the incoming CommandName value because any Command event raised by any Button, LinkButton, or ImageButton within the Repeater will cause the ItemCommand event to fire. Хотя сейчас у нас есть только одна такая LinkButton, в будущем мы (или другой разработчик в нашей группе) могут добавить в Repeater дополнительные веб-элементы управления для кнопок, которые при нажатии вызывают тот же ItemCommand обработчик событий.While we currently only have one such LinkButton now, in the future we (or another developer on our team) might add additional button Web controls to the Repeater that, when clicked, raises the same ItemCommand event handler. Поэтому рекомендуется всегда проверять свойство CommandName и продолжать только программную логику, если она соответствует ожидаемому значению.Therefore, it s best to always make sure you check the CommandName property and only proceed with your programmatic logic if it matches up to the value expected.

Убедившись, что переданное CommandName значение равно Листпродуктс, обработчик событий присваивает CategoryProductsDataSource ObjectDataSource s CategoryID параметру переданного CommandArgument.After ensuring that the passed-in CommandName value equals ListProducts, the event handler then assigns the CategoryProductsDataSource ObjectDataSource s CategoryID parameter to the value of the passed-in CommandArgument. Это изменение в ObjectDataSource SelectParameters автоматически приводит к повторной привязке элемента управления DataList к источнику данных, отображая продукты для вновь выбранной категории.This modification to the ObjectDataSource s SelectParameters automatically causes the DataList to rebind itself to the data source, showing the products for the newly selected category.

protected void Categories_ItemCommand(object source, RepeaterCommandEventArgs e)
{
    // If it's the "ListProducts" command that has been issued...
    if (string.Compare(e.CommandName, "ListProducts", true) == 0)
    {
        // Set the CategoryProductsDataSource ObjectDataSource's CategoryID parameter
        // to the CategoryID of the category that was just clicked (e.CommandArgument)...
        CategoryProductsDataSource.SelectParameters["CategoryID"].DefaultValue =
            e.CommandArgument.ToString();
    }
}

С этими дополнениями наш учебник завершен.With these additions, our tutorial is complete! Уделите немного времени для тестирования в браузере.Take a moment to test it out in a browser. На рис. 14 показан экран при первом посещении страницы.Figure 14 shows the screen when first visiting the page. Поскольку Категория еще не выбрана, продукты не отображаются.Since a category has yet to be selected, no products are displayed. Щелкнув категорию, например создать, вы увидите эти продукты в категории продуктов в представлении с двумя столбцами (см. рис. 15).Clicking on a category, such as Produce, displays those products in the Product category in a two-column view (see Figure 15).

при первом посещении страницы продукты не отображаютсяNo Products are Displayed When First Visiting the Page

Рис. 14. при первом посещении страницы продукты не отображаются (щелкните, чтобы просмотреть изображение с полным размером)Figure 14: No Products are Displayed When First Visiting the Page (Click to view full-size image)

щелкнув категорию создать, вы выведете список соответствующих продуктов справа.Clicking the Produce Category Lists the Matching Products to the Right

Рис. 15. Если щелкнуть категорию создать, выводится список соответствующих продуктов справа (щелкните, чтобы просмотреть изображение с полным размером).Figure 15: Clicking the Produce Category Lists the Matching Products to the Right (Click to view full-size image)

СводкаSummary

Как было показано в этом учебнике, а также на предыдущем, отчеты «основной/подробности» могут быть распределены между двумя страницами или объединены по одной.As we saw in this tutorial and the preceding one, master/detail reports can be spread out across two pages, or consolidated on one. При отображении отчета «основной/подробности» на одной странице приводятся некоторые трудности, связанные с тем, как лучше разрешать макеты основных и подробных записей на странице.Displaying a master/details report on a single page, however, introduces some challenges on how best to layout the master and details records on the page. В главной и подробной среде с помощью выбираемого главного элемента управления GridView с руководством по DetailsView подробные записи отображаются над главными записями. в этом учебнике мы использовали методы CSS, чтобы записи главной таблицы настроились слева от сведений.In the Master/Detail Using a Selectable Master GridView with a Details DetailsView tutorial we had the details records appear above the master records; in this tutorial we used CSS techniques to have the master records float to the left of the details.

Вместе с отображением отчетов «основной/подробности» у нас также была возможность исследовать, как получить количество продуктов, связанных с каждой категорией, а также как выполнять логику на стороне сервера при щелчке элемента LinkButton (или кнопки или ImageButton) в элементе Repeater.Along with displaying master/details reports, we also had the opportunity to explore how to retrieve the number of products associated with each category as well as how to perform server-side logic when a LinkButton (or Button or ImageButton) is clicked from within a Repeater.

Этот учебник завершает исследование отчетов «основной/подробности» с помощью элементов управления DataList и Repeater.This tutorial completes our examination of master/detail reports with the DataList and Repeater. В следующем наборе руководств показано, как добавить возможности редактирования и удаления элементов управления DataList.Our next set of tutorials will illustrate how to add editing and deleting capabilities to the DataList control.

Поздравляем с программированием!Happy Programming!

Дополнительные материалыFurther Reading

Дополнительные сведения о разделах, обсуждаемых в этом руководстве, см. в следующих ресурсах:For more information on the topics discussed in this tutorial, refer to the following resources:

Об автореAbout the Author

Скотт Митчелл, автор семи книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями Майкрософт с 1998.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Скотт работает как независимый консультант, преподаватель и модуль записи.Scott works as an independent consultant, trainer, and writer. Его последняя книга — Sams обучать себя ASP.NET 2,0 за 24 часа.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Он доступен по адресу mitchell@4GuysFromRolla.com.He can be reached at mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.or via his blog, which can be found at http://ScottOnWriting.NET.

Специальная благодарностьSpecial Thanks To

Эта серия руководств была рассмотрена многими полезными рецензентами.This tutorial series was reviewed by many helpful reviewers. Специалистом по интересу для этого учебника был Зак Джонс.Lead reviewer for this tutorial was Zack Jones. Хотите ознакомиться с моими будущими статьями MSDN?Interested in reviewing my upcoming MSDN articles? Если это так, расположите строку в mitchell@4GuysFromRolla.com.If so, drop me a line at mitchell@4GuysFromRolla.com.