Udostępnij za pośrednictwem


Wstawianie nowego rekordu w stopce kontrolki GridView (C#)

Autor : Scott Mitchell

Pobierz plik PDF

Chociaż kontrolka GridView nie zapewnia wbudowanej obsługi wstawiania nowego rekordu danych, w tym samouczku przedstawiono sposób rozszerzania kontrolki GridView w celu uwzględnienia interfejsu wstawiania.

Wprowadzenie

Zgodnie z opisem w samouczku Omówienie wstawiania, aktualizowania i usuwania danych kontrolki GridView, DetailsView i FormView w sieci Web obejmują wbudowane możliwości modyfikacji danych. W przypadku używania z deklaratywnymi kontrolkami źródła danych te trzy kontrolki sieci Web można szybko i łatwo skonfigurować do modyfikowania danych — i w scenariuszach bez konieczności pisania pojedynczego wiersza kodu. Niestety tylko kontrolki DetailsView i FormView zapewniają wbudowane funkcje wstawiania, edytowania i usuwania. Obiekt GridView oferuje tylko obsługę edytowania i usuwania. Jednak przy odrobinie smaru łokcia możemy rozszerzyć GridView, aby uwzględnić interfejs wstawiania.

Dodając możliwości wstawiania do kontrolki GridView, odpowiadamy za podjęcie decyzji o sposobie dodawania nowych rekordów, tworzeniu interfejsu wstawiania i pisaniu kodu w celu wstawienia nowego rekordu. W tym samouczku przyjrzymy się dodaniu interfejsu wstawiania do wiersza stopki kontrolki GridView (zobacz Rysunek 1). Komórka stopki dla każdej kolumny zawiera odpowiedni element interfejsu użytkownika zbierania danych (TextBox dla nazwy produktu, DropDownList dla dostawcy itd.). Potrzebujemy również kolumny przycisku Dodaj, który po kliknięciu spowoduje powrót i wstawi nowy rekord do Products tabeli przy użyciu wartości podanych w wierszu stopki.

Wiersz stopki zawiera interfejs dodawania nowych produktów

Rysunek 1. Wiersz stopki zawiera interfejs dodawania nowych produktów (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 1. Wyświetlanie informacji o produkcie w siatceView

Zanim zaczniemy się martwić o utworzenie interfejsu wstawiania w stopce kontrolki GridView, najpierw skoncentrujmy się na dodaniu kontrolki GridView do strony zawierającej listę produktów w bazie danych. Zacznij od otwarcia InsertThroughFooter.aspx strony w folderze EnhancedGridView i przeciągnięcia kontrolki GridView z przybornika do Projektant, ustawiając właściwość GridView ID na Productswartość . Następnie użyj tagu inteligentnego GridView, aby powiązać go z nowym obiektem ObjectDataSource o nazwie ProductsDataSource.

Tworzenie nowego obiektuDataSource o nazwie ProductsDataSource

Rysunek 2. Tworzenie nowego obiektuDataSource o nazwie ProductsDataSource (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Skonfiguruj obiekt ObjectDataSource do pobierania informacji o produkcie przy ProductsBLL użyciu metody s GetProducts() klasy . W tym samouczku skoncentrujmy się ściśle na dodawaniu funkcji wstawiania i nie martwmy się o edytowanie i usuwanie. W związku z tym upewnij się, że lista rozwijana na karcie INSERT jest ustawiona AddProduct() na i że listy rozwijane na kartach UPDATE i DELETE są ustawione na (Brak) .

Mapuj metodę AddProduct na metodę Insert() ObjectDataSource

Rysunek 3. Mapowanie AddProduct metody na metodę ObjectDataSource Insert() (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Ustaw Drop-Down Listy kart UPDATE i DELETE na wartość (Brak)

Rysunek 4. Ustawianie kart UPDATE i DELETE Drop-Down Listy na (Brak) (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Po zakończeniu pracy kreatora Konfigurowanie źródła danych objectDataSource program Visual Studio automatycznie doda pola do kontrolki GridView dla każdego z odpowiednich pól danych. Na razie pozostaw wszystkie pola dodane przez program Visual Studio. W dalszej części tego samouczka wrócimy i usuniemy niektóre pola, których wartości nie muszą być określone podczas dodawania nowego rekordu.

Ponieważ w bazie danych znajduje się blisko 80 produktów, użytkownik będzie musiał przewijać całą dół do dołu strony internetowej, aby dodać nowy rekord. W związku z tym włączmy stronicowanie, aby interfejs wstawiania był bardziej widoczny i dostępny. Aby włączyć stronicowanie, zaznacz pole wyboru Włącz stronicowanie z tagu inteligentnego GridView.

Na tym etapie znaczniki deklaratywne gridView i ObjectDataSource powinny wyglądać podobnie do następujących:

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="ProductID" HeaderText="ProductID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" HeaderText="ProductName" 
            SortExpression="ProductName" />
        <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" 
            SortExpression="SupplierID" />
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit" 
            SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" 
            SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" 
            SortExpression="UnitsInStock" />
        <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder" 
            SortExpression="UnitsOnOrder" />
        <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel" 
            SortExpression="ReorderLevel" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" 
            SortExpression="Discontinued" />
        <asp:BoundField DataField="CategoryName" HeaderText="CategoryName" 
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="SupplierName" 
            ReadOnly="True" SortExpression="SupplierName" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    InsertMethod="AddProduct" OldValuesParameterFormatString="original_{0}" 
    SelectMethod="GetProducts" TypeName="ProductsBLL">
    <InsertParameters>
        <asp:Parameter Name="productName" Type="String" />
        <asp:Parameter Name="supplierID" Type="Int32" />
        <asp:Parameter Name="categoryID" Type="Int32" />
        <asp:Parameter Name="quantityPerUnit" Type="String" />
        <asp:Parameter Name="unitPrice" Type="Decimal" />
        <asp:Parameter Name="unitsInStock" Type="Int16" />
        <asp:Parameter Name="unitsOnOrder" Type="Int16" />
        <asp:Parameter Name="reorderLevel" Type="Int16" />
        <asp:Parameter Name="discontinued" Type="Boolean" />
    </InsertParameters>
</asp:ObjectDataSource>

Wszystkie pola danych produktu są wyświetlane w siatce stronicowanej

Rysunek 5. Wszystkie pola danych produktu są wyświetlane w stronicowanym kącie GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)

Wraz z nagłówkiem i wierszami danych element GridView zawiera wiersz stopki. Wiersze nagłówka i stopki są wyświetlane w zależności od wartości właściwości i ShowFooter gridViewShowHeader. Aby wyświetlić wiersz stopki, po prostu ustaw ShowFooter właściwość na truewartość . Jak pokazano na rysunku 6, ustawienie właściwości w ShowFooter celu true dodawania wiersza stopki do siatki.

Aby wyświetlić wiersz stopki, ustaw wartość ShowFooter na true

Rysunek 6. Aby wyświetlić wiersz stopki, ustaw ShowFooter na True (kliknij, aby wyświetlić obraz pełnowymiarowy)

Zwróć uwagę, że wiersz stopki ma ciemnoczerwony kolor tła. Jest to spowodowane utworzonym motywem DataWebControls i zastosowanym do wszystkich stron z powrotem w samouczku Wyświetlanie danych za pomocą obiektuDataSource . W szczególności GridView.skin plik konfiguruje FooterStyle właściwość tak, aby używała FooterStyle klasy CSS. Klasa jest zdefiniowana FooterStyle w Styles.css następujący sposób:

.FooterStyle
{
    background-color: #a33;
    color: White;
    text-align: right;
}

Uwaga

W poprzednich samouczkach zapoznaliśmy się z użyciem wiersza stopki kontrolki GridView. W razie potrzeby zapoznaj się z samouczkiem Wyświetlanie informacji podsumowania w stopce kontrolki GridView w celu odświeżenia.

Po ustawieniu ShowFooter właściwości na true, poświęć chwilę, aby wyświetlić dane wyjściowe w przeglądarce. Obecnie wiersz stopki nie zawiera żadnych kontrolek tekstowych ani internetowych. W kroku 3 zmodyfikujemy stopkę dla każdego pola GridView, tak aby zawierała odpowiedni interfejs wstawiania.

Pusty wiersz stopki jest wyświetlany powyżej kontrolek interfejsu stronicowania

Rysunek 7. Pusty wiersz stopki jest wyświetlany powyżej kontrolek interfejsu stronicowania (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

W samouczku Dotyczącym używania pól szablonów w kontrolce GridView pokazano, jak znacznie dostosować wyświetlanie określonej kolumny GridView przy użyciu kontrolek TemplateFields (w przeciwieństwie do pól BoundFields lub CheckBoxFields); W dostosowywaniu interfejsu modyfikacji danych przyjrzeliśmy się używaniu elementu TemplateFields w celu dostosowania interfejsu edycji w elementy GridView. Pamiętaj, że pole templatefield składa się z wielu szablonów, które definiują kombinację znaczników, kontrolek sieci Web i składni powiązania danych używanych dla niektórych typów wierszy. Na ItemTemplateprzykład , określa szablon używany dla wierszy tylko do odczytu, podczas gdy EditItemTemplate definiuje szablon dla edytowalnego wiersza.

Wraz z elementami ItemTemplate i EditItemTemplateelement TemplateField zawiera również element FooterTemplate , który określa zawartość wiersza stopki. W związku z tym możemy dodać kontrolki sieci Web potrzebne dla każdego pola interfejsu wstawiania do elementu FooterTemplate. Aby rozpocząć, przekonwertuj wszystkie pola w elementy GridView na TemplateFields. Można to zrobić, klikając link Edytuj kolumny w tagu inteligentnym GridView, wybierając każde pole w lewym dolnym rogu, a następnie klikając link Konwertuj to pole na pole szablonu.

Konwertowanie każdego pola na pole szablonu

Rysunek 8. Konwertowanie każdego pola na pole szablonu

Kliknięcie pola Konwertuj to pole na pole szablonu powoduje przekształcenie bieżącego typu pola w równoważne pole szablonu. Na przykład każde pole BoundField jest zastępowane przez pole TemplateField elementem ItemTemplate zawierającym etykietę, która wyświetla odpowiednie pole danych i EditItemTemplate wyświetla pole danych w polu tekstowym. Pole ProductName boundfield zostało przekonwertowane na następujące znaczniki TemplateField:

<asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
    <EditItemTemplate>
        <asp:TextBox ID="TextBox1" runat="server" 
            Text='<%# Bind("ProductName") %>'></asp:TextBox>
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Label ID="Label2" runat="server" 
            Text='<%# Bind("ProductName") %>'></asp:Label>
    </ItemTemplate>
</asp:TemplateField>

Podobnie Discontinued pole CheckBoxField zostało przekonwertowane na pole szablonu, którego ItemTemplate i EditItemTemplate zawiera kontrolkę internetową CheckBox (z wyłączonym ItemTemplate polem kontrolnym). Pole BoundField tylko ProductID do odczytu zostało przekonwertowane na pole szablonu z kontrolką Etykieta zarówno w kontrolce , jak ItemTemplate i EditItemTemplate. Krótko mówiąc, konwertowanie istniejącego pola GridView na pole TemplateField to szybki i łatwy sposób przełączania się na bardziej dostosowywalne pole TemplateField bez utraty żadnej funkcji istniejącego pola.

Ponieważ element GridView, z który pracujemy, nie obsługuje edycji, możesz usunąć element EditItemTemplate z każdego pola szablonu, pozostawiając tylko element ItemTemplate. Po wykonaniu tej czynności znacznik deklaratywny gridView powinien wyglądać następująco:

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" EnableViewState="False" ShowFooter="True">
    <Columns>
        <asp:TemplateField HeaderText="ProductID" InsertVisible="False" 
            SortExpression="ProductID">
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" 
                    Text='<%# Bind("ProductID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server" 
                    Text='<%# Bind("ProductName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="SupplierID" SortExpression="SupplierID">
            <ItemTemplate>
                <asp:Label ID="Label3" runat="server" 
                    Text='<%# Bind("SupplierID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="CategoryID" SortExpression="CategoryID">
            <ItemTemplate>
                <asp:Label ID="Label4" runat="server" 
                    Text='<%# Bind("CategoryID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="QuantityPerUnit" 
            SortExpression="QuantityPerUnit">
            <ItemTemplate>
                <asp:Label ID="Label5" runat="server" 
                    Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="UnitPrice" SortExpression="UnitPrice">
            <ItemTemplate>
                <asp:Label ID="Label6" runat="server" 
                    Text='<%# Bind("UnitPrice") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="UnitsInStock" 
            SortExpression="UnitsInStock">
            <ItemTemplate>
                <asp:Label ID="Label7" runat="server" 
                    Text='<%# Bind("UnitsInStock") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="UnitsOnOrder" 
            SortExpression="UnitsOnOrder">
            <ItemTemplate>
                <asp:Label ID="Label8" runat="server" 
                    Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="ReorderLevel" 
            SortExpression="ReorderLevel">
            <ItemTemplate>
                <asp:Label ID="Label9" runat="server" 
                    Text='<%# Bind("ReorderLevel") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Discontinued" 
            SortExpression="Discontinued">
            <ItemTemplate>
                <asp:CheckBox ID="CheckBox1" runat="server" 
                    Checked='<%# Bind("Discontinued") %>' Enabled="false" />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="CategoryName" 
            SortExpression="CategoryName">
            <ItemTemplate>
                <asp:Label ID="Label10" runat="server" 
                    Text='<%# Bind("CategoryName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="SupplierName" 
            SortExpression="SupplierName">
            <ItemTemplate>
                <asp:Label ID="Label11" runat="server" 
                    Text='<%# Bind("SupplierName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Teraz, gdy każde pole GridView zostało przekonwertowane na pole szablonu, możemy wprowadzić odpowiedni interfejs wstawiania do każdego pola s FooterTemplate. Niektóre pola nie będą miały interfejsu wstawiania (ProductIDna przykład); inne będą się różnić w kontrolkach sieci Web używanych do zbierania informacji o nowym produkcie.

Aby utworzyć interfejs edycji, wybierz link Edytuj szablony z tagu inteligentnego GridView. Następnie z listy rozwijanej wybierz odpowiednie pola FooterTemplate i przeciągnij odpowiednią kontrolkę z przybornika na Projektant.

Dodawanie odpowiedniego interfejsu wstawiania do stopki każdego polaTemplate

Rysunek 9. Dodawanie odpowiedniego interfejsu wstawiania do każdego pola FooterTemplate (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Poniższa lista punktowana wylicza pola GridView, określając interfejs wstawiania do dodania:

  • ProductID Brak.
  • ProductName dodaj kontrolkę TextBox i ustaw jej ID wartość na NewProductName. Dodaj również kontrolkę RequiredFieldValidator, aby upewnić się, że użytkownik wprowadzi wartość dla nazwy nowego produktu.
  • SupplierID Brak.
  • CategoryID Brak.
  • QuantityPerUnit dodaj kontrolkę TextBox, ustawiając jej ID wartość na NewQuantityPerUnit.
  • UnitPrice Dodaj element TextBox o nazwie NewUnitPrice i element CompareValidator, który zapewnia, że wprowadzona wartość jest wartością waluty większą lub równą zero.
  • UnitsInStock użyj kontrolki TextBox, której ID parametr ma ustawioną wartość NewUnitsInStock. Uwzględnij moduł CompareValidator, który gwarantuje, że wprowadzona wartość jest liczbą całkowitą większą lub równą zero.
  • UnitsOnOrder użyj kontrolki TextBox, której ID parametr ma ustawioną wartość NewUnitsOnOrder. Uwzględnij moduł CompareValidator, który gwarantuje, że wprowadzona wartość jest liczbą całkowitą większą lub równą zero.
  • ReorderLevel użyj kontrolki TextBox, której ID parametr ma ustawioną wartość NewReorderLevel. Uwzględnij moduł CompareValidator, który gwarantuje, że wprowadzona wartość jest liczbą całkowitą większą lub równą zero.
  • Discontinued dodaj kontrolkę CheckBox, ustawiając jej ID wartość na NewDiscontinued.
  • CategoryName dodaj listę DropDownList i ustaw ją ID na NewCategoryID. Powiąż go z nową klasą ObjectDataSource o nazwie CategoriesDataSource i skonfiguruj ją tak, aby korzystała CategoriesBLL z metody klasy s GetCategories() . Wyświetl pole danych za CategoryName pomocą pola danych z listy DropDownListListItem, używając CategoryID pola danych jako wartości.
  • SupplierName dodaj listę DropDownList i ustaw ją ID na NewSupplierID. Powiąż go z nową klasą ObjectDataSource o nazwie SuppliersDataSource i skonfiguruj ją tak, aby korzystała SuppliersBLL z metody klasy s GetSuppliers() . Wyświetl pole danych za CompanyName pomocą pola danych z listy DropDownListListItem, używając SupplierID pola danych jako wartości.

Dla każdej kontrolki walidacji usuń ForeColor zaznaczenie właściwości, tak aby FooterStyle biały kolor pierwszego planu klasy CSS był używany zamiast domyślnego czerwonego koloru. Należy również użyć ErrorMessage właściwości w celu uzyskania szczegółowego opisu, ale ustaw Text właściwość na gwiazdkę. Aby zapobiec tworzeniu tekstu kontrolki sprawdzania poprawności powodującym zawijanie interfejsu wstawiania do dwóch wierszy, ustaw FooterStyle właściwość s Wrap na wartość false dla każdego z FooterTemplate elementów, które używają kontrolki walidacji. Na koniec dodaj kontrolkę ValidationSummary poniżej kontrolki GridView i ustaw jej ShowMessageBox właściwość na true i jej ShowSummary właściwość na false.

Podczas dodawania nowego produktu musimy podać elementy CategoryID i SupplierID. Te informacje są przechwytywane za pośrednictwem listy DropDownLists w komórkach stopki dla CategoryName pól i SupplierName . Zdecydowałem się używać tych pól w przeciwieństwie do CategoryID pól szablonów i SupplierID , ponieważ w wierszach danych siatki użytkownik jest prawdopodobnie bardziej zainteresowany wyświetlaniem kategorii i nazw dostawców, a nie ich wartości identyfikatorów. CategoryID Ponieważ wartości i SupplierID są teraz przechwytywane w CategoryName interfejsach wstawiania pól iSupplierName, możemy usunąć pola CategoryID i SupplierID z obiektu GridView.

Podobnie element ProductID nie jest używany podczas dodawania nowego produktu, więc pole szablonu ProductID można również usunąć. Pozostawmy ProductID jednak pole w siatce. Oprócz kontrolek TextBoxes, DropDownLists, CheckBoxes i validation, które tworzą interfejs wstawiania, potrzebujemy również przycisku Dodaj, który po kliknięciu wykonuje logikę dodawania nowego produktu do bazy danych. W kroku 4 dołączymy przycisk Dodaj do interfejsu wstawiania w elemecie ProductID TemplateField.FooterTemplate

Możesz poprawić wygląd różnych pól GridView. Możesz na przykład sformatować UnitPrice wartości jako walutę, wyrównać UnitsInStockdo prawej pola , UnitsOnOrderi ReorderLevel oraz zaktualizować HeaderText wartości pól TemplateFields.

Po utworzeniu mnóstwa wstawień interfejsów w FooterTemplate s, usunięciu SupplierIDpól szablonów i CategoryID i poprawie estetyki siatki poprzez formatowanie i wyrównanie pól TemplateFields, znacznik deklaratywny GridView powinien wyglądać podobnie do następującego:

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" EnableViewState="False" ShowFooter="True">
    <Columns>
        <asp:TemplateField HeaderText="ProductID" InsertVisible="False" 
            SortExpression="ProductID">
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" 
                    Text='<%# Bind("ProductID") %>'></asp:Label>
            </ItemTemplate>
            <ItemStyle HorizontalAlign="Center" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server" 
                    Text='<%# Bind("ProductName") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewProductName" runat="server"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                    runat="server" ControlToValidate="NewProductName"
                    Display="Dynamic"  ForeColor="
                    ErrorMessage="You must enter a name for the new product.">
                    * </asp:RequiredFieldValidator>
            </FooterTemplate>
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
            <ItemTemplate>
                <asp:Label ID="Label10" runat="server" 
                    Text='<%# Bind("CategoryName") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:DropDownList ID="NewCategoryID" runat="server" 
                    DataSourceID="CategoriesDataSource"
                    DataTextField="CategoryName" DataValueField="CategoryID">
                </asp:DropDownList>
                <asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
                    OldValuesParameterFormatString="original_{0}" 
                    SelectMethod="GetCategories" TypeName="CategoriesBLL">
                </asp:ObjectDataSource>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
            <ItemTemplate>
                <asp:Label ID="Label11" runat="server" 
                    Text='<%# Bind("SupplierName") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:DropDownList ID="NewSupplierID" runat="server" 
                    DataSourceID="SuppliersDataSource"
                    DataTextField="CompanyName" DataValueField="SupplierID">
                </asp:DropDownList><asp:ObjectDataSource ID="SuppliersDataSource" 
                    runat="server" OldValuesParameterFormatString="original_{0}" 
                    SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
                </asp:ObjectDataSource>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Qty/Unit" SortExpression="QuantityPerUnit">
            <ItemTemplate>
                <asp:Label ID="Label5" runat="server" 
                    Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewQuantityPerUnit" runat="server"></asp:TextBox>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
            <ItemTemplate>
                <asp:Label ID="Label6" runat="server" 
                    Text='<%# Bind("UnitPrice", "{0:c}") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                $<asp:TextBox ID="NewUnitPrice" runat="server" Columns="8" />
                <asp:CompareValidator ID="CompareValidator1" runat="server" 
                    ControlToValidate="NewUnitPrice"
                    ErrorMessage="You must enter a valid currency value greater than 
                        or equal to 0.00. Do not include the currency symbol."
                    ForeColor="" Operator="GreaterThanEqual" Type="Currency" 
                    ValueToCompare="0" Display="Dynamic">
                    * </asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Units In Stock" 
            SortExpression="Units In Stock">
            <ItemTemplate>
                <asp:Label ID="Label7" runat="server" 
                    Text='<%# Bind("UnitsInStock") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewUnitsInStock" runat="server" Columns="5" />
                <asp:CompareValidator ID="CompareValidator2" runat="server" 
                    ControlToValidate="NewUnitsInStock" Display="Dynamic" 
                    ErrorMessage="You must enter a valid numeric value for units 
                        in stock that's greater than or equal to zero."
                    ForeColor="" Operator="GreaterThanEqual" Type="Integer" 
                        ValueToCompare="0">*</asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Units On Order" SortExpression="UnitsOnOrder">
            <ItemTemplate>
                <asp:Label ID="Label8" runat="server" 
                    Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewUnitsOnOrder" runat="server" Columns="5" />
                <asp:CompareValidator ID="CompareValidator3" runat="server" 
                    ControlToValidate="NewUnitsOnOrder" Display="Dynamic" 
                    ErrorMessage="You must enter a valid numeric value for units on 
                        order that's greater than or equal to zero."
                    ForeColor="" Operator="GreaterThanEqual" Type="Integer" 
                    ValueToCompare="0">*</asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Reorder Level" SortExpression="ReorderLevel">
            <ItemTemplate>
                <asp:Label ID="Label9" runat="server" 
                    Text='<%# Bind("ReorderLevel") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewReorderLevel" runat="server" Columns="5" />
                <asp:CompareValidator ID="CompareValidator4" runat="server" 
                    ControlToValidate="NewReorderLevel" Display="Dynamic" 
                    ErrorMessage="You must enter a valid numeric value for reorder 
                        level that's greater than or equal to zero."
                    ForeColor="" Operator="GreaterThanEqual" Type="Integer" 
                    ValueToCompare="0">*</asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
            <ItemTemplate>
                <asp:CheckBox ID="CheckBox1" runat="server" 
                    Checked='<%# Bind("Discontinued") %>' Enabled="false" />
            </ItemTemplate>
            <FooterTemplate>
                <asp:CheckBox ID="NewDiscontinued" runat="server" />
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Center" />
            <FooterStyle HorizontalAlign="Center" />
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Po wyświetleniu za pośrednictwem przeglądarki wiersz stopki kontrolki GridView zawiera teraz ukończony interfejs wstawiania (zobacz Rysunek 10). W tym momencie interfejs wstawiania nie zawiera środków dla użytkownika, aby wskazać, że wprowadziła dane dla nowego produktu i chce wstawić nowy rekord do bazy danych. Ponadto mamy jeszcze do uwzględnienia sposób, w jaki dane wprowadzone w stopce przełożą się na nowy rekord w Products bazie danych. W kroku 4 przyjrzymy się, jak dołączyć przycisk Dodaj do interfejsu wstawiania i jak wykonać kod po kliknięciu po kliknięciu. Krok 5 pokazuje, jak wstawić nowy rekord przy użyciu danych z stopki.

Stopka GridView udostępnia interfejs dodawania nowego rekordu

Rysunek 10. Stopka GridView udostępnia interfejs dodawania nowego rekordu (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 4. Dołączenie przycisku Dodaj w interfejsie wstawiania

Musimy dołączyć przycisk Dodaj gdzieś w interfejsie wstawiania, ponieważ interfejs wstawiania wierszy stopki obecnie nie ma środków dla użytkownika, aby wskazać, że zostały ukończone wprowadzanie informacji o nowym produkcie. Można to umieścić w jednym z istniejących FooterTemplate elementów lub dodać nową kolumnę do siatki w tym celu. W tym samouczku umieśćmy przycisk Dodaj w elemecie ProductID TemplateField s FooterTemplate.

W Projektant kliknij link Edytuj szablony w tagu inteligentnym GridView, a następnie wybierz ProductID pole FooterTemplate z listy rozwijanej. Dodaj kontrolkę Sieci Web przycisku (lub LinkButton lub ImageButton, jeśli wolisz) do szablonu, ustawiając jego identyfikator na AddProduct, na CommandName Wstaw, a jej Text właściwość na Dodaj, jak pokazano na rysunku 11.

Umieść przycisk Dodaj w stopce productID TemplateField sTemplate

Rysunek 11. Umieść przycisk Dodaj w polach ProductID szablonów FooterTemplate (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Po dołączeniu przycisku Dodaj przetestuj stronę w przeglądarce. Należy pamiętać, że po kliknięciu przycisku Dodaj z nieprawidłowymi danymi w interfejsie wstawiania ogłaszanie zwrotne jest zwarcie, a kontrolka ValidationSummary wskazuje nieprawidłowe dane (zobacz Rysunek 12). Po wprowadzeniu odpowiednich danych kliknięcie przycisku Dodaj powoduje ogłaszanie zwrotne. Do bazy danych nie jest jednak dodawany żaden rekord. Musimy napisać trochę kodu, aby rzeczywiście wykonać operację wstawiania.

Ogłaszanie zwrotne przycisku Dodaj jest zwarcie, jeśli w interfejsie wstawiania znajdują się nieprawidłowe dane

Rysunek 12. Ogłaszanie zwrotne przycisku dodawania jest zwarcie, jeśli w interfejsie wstawiania znajdują się nieprawidłowe dane (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Uwaga

Kontrolki sprawdzania poprawności w interfejsie wstawiania nie zostały przypisane do grupy sprawdzania poprawności. Działa to poprawnie, o ile interfejs wstawiania jest jedynym zestawem kontrolek sprawdzania poprawności na stronie. Jeśli jednak na stronie znajdują się inne kontrolki weryfikacji (takie jak kontrolki walidacji w interfejsie edycji siatki), kontrolki walidacji w interfejsie wstawiania i Dodaj właściwości przycisku ValidationGroup powinny mieć przypisaną tę samą wartość, aby skojarzyć te kontrolki z określoną grupą walidacji. Zobacz Artykuł Dissecting the Validation Controls in ASP.NET 2.0 (Rozłączanie kontrolek weryfikacji w programie ASP.NET 2.0 ), aby uzyskać więcej informacji na temat partycjonowania kontrolek i przycisków weryfikacji na stronie w grupach weryfikacji.

Krok 5. Wstawianie nowego rekordu doProductstabeli

W przypadku korzystania z wbudowanych funkcji edycji kontrolki GridView funkcja GridView automatycznie obsługuje całą pracę niezbędną do przeprowadzenia aktualizacji. W szczególności kliknięcie przycisku Aktualizuj powoduje skopiowanie wartości wprowadzonych z interfejsu edycji do parametrów w kolekcji ObjectDataSource UpdateParameters i rozpoczęcie aktualizacji przez wywołanie metody ObjectDataSource Update() . Ponieważ kontrolka GridView nie udostępnia takich wbudowanych funkcji do wstawiania, musimy zaimplementować kod, który wywołuje metodę ObjectDataSource i Insert() kopiuje wartości z interfejsu wstawiania do kolekcji ObjectDataSource InsertParameters .

Ta logika wstawiania powinna zostać wykonana po kliknięciu przycisku Dodaj. Zgodnie z opisem w samouczku Dodawanie przycisków i odpowiadanie na nie w siatce, w dowolnym momencie kliknięcie przycisku, elementu LinkButton lub elementu ImageButton w siatce, zdarzenie GridView RowCommand zostanie wyzwolone po ogłaszaniu zwrotnym. To zdarzenie jest wyzwalane, czy przycisk Button, LinkButton lub ImageButton został dodany jawnie, na przykład przycisk Dodaj w wierszu stopki lub jeśli został automatycznie dodany przez kontrolkę GridView (np. LinkButtons w górnej części każdej kolumny po wybraniu opcji Włącz sortowanie lub linkButtons w interfejsie stronicowania po wybraniu opcji Włącz stronicowanie).

W związku z tym, aby odpowiedzieć użytkownikowi klikając przycisk Dodaj, musimy utworzyć procedurę obsługi zdarzeń dla zdarzenia GridView RowCommand . Ponieważ to zdarzenie jest uruchamiane za każdym razem, gdy zostanie kliknięto dowolny przycisk Button, LinkButton lub ImageButton w siatceView, ważne jest, aby kontynuować tylko logikę wstawiania, jeśli CommandName właściwość przekazana do programu obsługi zdarzeń jest mapowana na CommandName wartość przycisku Dodaj ( Wstaw ). Ponadto należy kontynuować tylko wtedy, gdy kontrolki weryfikacji zgłaszają prawidłowe dane. Aby to uwzględnić, utwórz program obsługi zdarzeń dla RowCommand zdarzenia przy użyciu następującego kodu:

protected void Products_RowCommand(object sender, GridViewCommandEventArgs e)
{
    // Insert data if the CommandName == "Insert" 
    // and the validation controls indicate valid data...
    if (e.CommandName == "Insert" && Page.IsValid)
    {
        // TODO: Insert new record...
    }
}

Uwaga

Być może zastanawiasz się, dlaczego program obsługi zdarzeń przeszkadza sprawdzanie Page.IsValid właściwości. Po tym wszystkim, czy ogłaszanie zwrotne nie zostanie pominięte, jeśli w interfejsie wstawiania podano nieprawidłowe dane? To założenie jest poprawne, o ile użytkownik nie wyłączył języka JavaScript lub podjął kroki w celu obejścia logiki weryfikacji po stronie klienta. Krótko mówiąc, nigdy nie należy polegać ściśle na weryfikacji po stronie klienta; sprawdzanie poprawności po stronie serwera powinno być zawsze wykonywane przed rozpoczęciem pracy z danymi.

W kroku 1 utworzyliśmy ProductsDataSource obiekt ObjectDataSource, tak aby jego Insert() metoda została zamapowana na metodę ProductsBLL klasy s AddProduct . Aby wstawić nowy rekord do Products tabeli, możemy po prostu wywołać metodę ObjectDataSource Insert() :

protected void Products_RowCommand(object sender, GridViewCommandEventArgs e)
{
    // Insert data if the CommandName == "Insert" 
    // and the validation controls indicate valid data...
    if (e.CommandName == "Insert" && Page.IsValid)
    {
        // Insert new record
        ProductsDataSource.Insert();
    }
}

Teraz, gdy Insert() metoda została wywołana, pozostaje skopiować wartości z interfejsu wstawiania do parametrów przekazanych do ProductsBLL metody klasy AddProduct . Jak widzieliśmy w samouczku Badanie zdarzeń skojarzonych z wstawianiem, aktualizowaniem i usuwaniem , można to zrobić za pomocą zdarzenia ObjectDataSource Inserting . Inserting W przypadku, gdy musimy programowo odwołać się do kontrolek z Products wiersza stopki kontrolki GridView i przypisać ich wartości do e.InputParameters kolekcji. Jeśli użytkownik pominie wartość, taką jak pozostawienie pola TextBox pustego ReorderLevel , musimy określić, że wartość wstawiona do bazy danych powinna mieć wartość NULL. AddProducts Ponieważ metoda akceptuje typy dopuszczane do wartości null dla pól bazy danych dopuszczalnych wartości null, po prostu użyj typu dopuszczalnego wartości null i ustaw jej wartość na null w przypadku pominięcia danych wejściowych użytkownika.

protected void ProductsDataSource_Inserting
    (object sender, ObjectDataSourceMethodEventArgs e)
{
    // Programmatically reference Web controls in the inserting interface...
    TextBox NewProductName = 
        (TextBox)Products.FooterRow.FindControl("NewProductName");
    DropDownList NewCategoryID = 
        (DropDownList)Products.FooterRow.FindControl("NewCategoryID");
    DropDownList NewSupplierID = 
        (DropDownList)Products.FooterRow.FindControl("NewSupplierID");
    TextBox NewQuantityPerUnit = 
        (TextBox)Products.FooterRow.FindControl("NewQuantityPerUnit");
    TextBox NewUnitPrice = 
        (TextBox)Products.FooterRow.FindControl("NewUnitPrice");
    TextBox NewUnitsInStock = 
        (TextBox)Products.FooterRow.FindControl("NewUnitsInStock");
    TextBox NewUnitsOnOrder = 
        (TextBox)Products.FooterRow.FindControl("NewUnitsOnOrder");
    TextBox NewReorderLevel = 
        (TextBox)Products.FooterRow.FindControl("NewReorderLevel");
    CheckBox NewDiscontinued = 
        (CheckBox)Products.FooterRow.FindControl("NewDiscontinued");
    // Set the ObjectDataSource's InsertParameters values...
    e.InputParameters["productName"] = NewProductName.Text;
    
    e.InputParameters["supplierID"] = 
        Convert.ToInt32(NewSupplierID.SelectedValue);
    e.InputParameters["categoryID"] = 
        Convert.ToInt32(NewCategoryID.SelectedValue);
    
    string quantityPerUnit = null;
    if (!string.IsNullOrEmpty(NewQuantityPerUnit.Text))
        quantityPerUnit = NewQuantityPerUnit.Text;
    e.InputParameters["quantityPerUnit"] = quantityPerUnit;
    decimal? unitPrice = null;
    if (!string.IsNullOrEmpty(NewUnitPrice.Text))
        unitPrice = Convert.ToDecimal(NewUnitPrice.Text);
    e.InputParameters["unitPrice"] = unitPrice;
    short? unitsInStock = null;
    if (!string.IsNullOrEmpty(NewUnitsInStock.Text))
        unitsInStock = Convert.ToInt16(NewUnitsInStock.Text);
    e.InputParameters["unitsInStock"] = unitsInStock;
    short? unitsOnOrder = null;
    if (!string.IsNullOrEmpty(NewUnitsOnOrder.Text))
        unitsOnOrder = Convert.ToInt16(NewUnitsOnOrder.Text);
    e.InputParameters["unitsOnOrder"] = unitsOnOrder;
    short? reorderLevel = null;
    if (!string.IsNullOrEmpty(NewReorderLevel.Text))
        reorderLevel = Convert.ToInt16(NewReorderLevel.Text);
    e.InputParameters["reorderLevel"] = reorderLevel;
    
    e.InputParameters["discontinued"] = NewDiscontinued.Checked;
}

Po zakończeniu Inserting obsługi zdarzeń można dodać nowe rekordy do Products tabeli bazy danych za pośrednictwem wiersza stopki kontrolki GridView. Przejdź dalej i spróbuj dodać kilka nowych produktów.

Ulepszanie i dostosowywanie operacji dodawania

Obecnie kliknięcie przycisku Dodaj powoduje dodanie nowego rekordu do tabeli bazy danych, ale nie udostępnia żadnych wizualnych opinii, które rekord został pomyślnie dodany. W idealnym przypadku kontrolka Sieci Web etykiety lub pole alertu po stronie klienta informuje użytkownika, że jego wstawianie zakończyło się pomyślnie. Zostawię to jako ćwiczenie dla czytelnika.

Obiekt GridView używany w tym samouczku nie stosuje żadnej kolejności sortowania do wymienionych produktów ani nie zezwala użytkownikowi końcowemu na sortowanie danych. W związku z tym rekordy są uporządkowane zgodnie z ich polem klucza podstawowego w bazie danych. Ponieważ każdy nowy rekord ma ProductID wartość większą niż ostatni, za każdym razem, gdy nowy produkt jest dodawany, jest on na końcu siatki. W związku z tym możesz automatycznie wysłać użytkownika do ostatniej strony obiektu GridView po dodaniu nowego rekordu. Można to zrobić, dodając następujący wiersz kodu po wywołaniu metody ProductsDataSource.Insert() w procedurze obsługi zdarzeń, RowCommand aby wskazać, że użytkownik musi zostać wysłany do ostatniej strony po powiązaniu danych z kontrolką GridView:

// Indicate that the user needs to be sent to the last page
SendUserToLastPage = true;

SendUserToLastPage to zmienna logiczna na poziomie strony, która początkowo ma przypisaną wartość false. W procedurze obsługi zdarzeń kontrolki DataBound GridView, jeśli SendUserToLastPage ma wartość false, właściwość zostanie zaktualizowana w PageIndex celu wysłania użytkownika do ostatniej strony.

protected void Products_DataBound(object sender, EventArgs e)
{
    // Send user to last page of data, if needed
    if (SendUserToLastPage)
        Products.PageIndex = Products.PageCount - 1;
}

Przyczyną PageIndex ustawienia właściwości w procedurze DataBound obsługi zdarzeń (w przeciwieństwie do RowCommand programu obsługi zdarzeń) jest to, że gdy RowCommand program obsługi zdarzeń uruchomi jeszcze nowy rekord do Products tabeli bazy danych. W związku z tym w procedurze obsługi zdarzeń RowCommand ostatni indeks strony (PageCount - 1) reprezentuje ostatni indeks strony przed dodaniu nowego produktu. W przypadku większości dodawanych produktów ostatni indeks strony jest taki sam po dodaniu nowego produktu. Ale gdy dodany produkt spowoduje wyświetlenie nowego indeksu ostatniej strony, jeśli niepoprawnie zaktualizujemy PageIndex element w RowCommand procedurze obsługi zdarzeń, nastąpi przekierowanie do drugiej do ostatniej strony (indeks ostatniej strony przed dodaniem nowego produktu) w przeciwieństwie do nowego indeksu ostatniej strony. DataBound Ponieważ procedura obsługi zdarzeń jest uruchamiana po dodaniu nowego produktu i odbiciu danych do siatki, ustawiając PageIndex właściwość , wiemy, że otrzymujemy prawidłowy indeks ostatniej strony.

Na koniec element GridView używany w tym samouczku jest dość szeroki ze względu na liczbę pól, które należy zebrać w celu dodania nowego produktu. Ze względu na tę szerokość preferowany może być układ pionowy kontrolki DetailsView. Ogólną szerokość obiektu GridView można zmniejszyć, zbierając mniej danych wejściowych. Być może nie musimy zbierać UnitsOnOrderpól , UnitsInStocki ReorderLevel podczas dodawania nowego produktu, w tym przypadku te pola można usunąć z widoku GridView.

Aby dostosować zebrane dane, możemy użyć jednego z dwóch podejść:

  • Kontynuuj korzystanie z AddProduct metody, która oczekuje wartości dla UnitsOnOrderpól , UnitsInStocki ReorderLevel . W procedurze obsługi zdarzeń Inserting podaj zakodowane wartości domyślne, które mają być używane dla tych danych wejściowych, które zostały usunięte z interfejsu wstawiania.
  • Utwórz nowe przeciążenie AddProduct metody w ProductsBLL klasie, która nie akceptuje danych wejściowych dla UnitsOnOrderpól , UnitsInStocki ReorderLevel . Następnie na stronie ASP.NET skonfiguruj obiekt ObjectDataSource do używania tego nowego przeciążenia.

Każda opcja również będzie działać równie dobrze. W poprzednich samouczkach użyliśmy tej drugiej opcji, tworząc wiele przeciążeń dla ProductsBLL metody klasy s UpdateProduct .

Podsumowanie

Element GridView nie ma wbudowanych funkcji wstawiania znajdujących się w widoku DetailsView i FormView, ale z odrobiną wysiłku można dodać interfejs wstawiania do wiersza stopki. Aby wyświetlić wiersz stopki w elemecie GridView, po prostu ustaw jej ShowFooter właściwość na true. Zawartość wiersza stopki można dostosować dla każdego pola, konwertując pole na wartość TemplateField i dodając interfejs wstawiania do elementu FooterTemplate. Jak pokazano w tym samouczku, FooterTemplate może zawierać kontrolki Buttons, TextBoxes, DropDownLists, CheckBoxes, data source controls for populating data-driven Web controls (takie jak DropDownLists) i kontrolek walidacji. Oprócz kontrolek do zbierania danych wejściowych użytkownika jest wymagany przycisk Dodaj, LinkButton lub ImageButton.

Po kliknięciu przycisku Dodaj wywoływana jest metoda ObjectDataSource Insert() , aby rozpocząć wstawianie przepływu pracy. Następnie obiekt ObjectDataSource wywoła skonfigurowaną metodę insert ( ProductsBLL metodę klasy s AddProduct w tym samouczku). Musimy skopiować wartości z interfejsu wstawiania elementu GridView do kolekcji ObjectDataSource InsertParameters przed wywołaniem metody insert. Można to osiągnąć przez programowe odwoływanie się do wstawiania kontrolek sieci Web interfejsu w programie obsługi zdarzeń objectDataSource Inserting .

W tym samouczku przedstawiono techniki ulepszania wyglądu elementu GridView. Następny zestaw samouczków sprawdzi, jak pracować z danymi binarnymi, takimi jak obrazy, pliki PDF, Word dokumenty itd. i kontrolki sieci Web danych.

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została sprawdzona przez wielu pomocnych recenzentów. Główny recenzent tego samouczka był Bernadette Leigh. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresem mitchell@4GuysFromRolla.com.