GridView의 바닥글에서 새 레코드 삽입(VB)

작성자 : Scott Mitchell

PDF 다운로드

GridView 컨트롤은 새 데이터 레코드 삽입에 대한 기본 제공 지원을 제공하지 않지만 이 자습서에서는 삽입 인터페이스를 포함하도록 GridView를 보강하는 방법을 보여줍니다.

소개

데이터 삽입, 업데이트 및 삭제 개요 자습서에서 설명한 대로 GridView, DetailsView 및 FormView 웹 컨트롤에는 각각 기본 제공 데이터 수정 기능이 포함됩니다. 선언적 데이터 소스 컨트롤과 함께 사용할 경우 이러한 세 가지 웹 컨트롤은 데이터를 수정하도록 빠르고 쉽게 구성할 수 있으며, 시나리오에서는 한 줄의 코드를 작성할 필요 없이 구성할 수 있습니다. 아쉽게도 DetailsView 및 FormView 컨트롤만 기본 제공 삽입, 편집 및 삭제 기능을 제공합니다. GridView는 편집 및 삭제 지원만 제공합니다. 그러나 약간의 팔꿈치 그리스를 사용하면 삽입 인터페이스를 포함하도록 GridView를 보강할 수 있습니다.

GridView에 삽입 기능을 추가할 때 새 레코드를 추가하는 방법을 결정하고, 삽입 인터페이스를 만들고, 새 레코드를 삽입하는 코드를 작성해야 합니다. 이 자습서에서는 GridView 바닥글 행에 삽입 인터페이스를 추가하는 방법을 살펴봅니다(그림 1 참조). 각 열의 바닥글 셀에는 적절한 데이터 컬렉션 사용자 인터페이스 요소(제품 이름에 대한 TextBox, 공급자에 대한 DropDownList 등)가 포함됩니다. 또한 추가 단추에 대한 열이 필요합니다. 이 단추를 클릭하면 포스트백이 발생하고 바닥글 행에 Products 제공된 값을 사용하여 테이블에 새 레코드를 삽입합니다.

바닥글 행은 새 제품을 추가하기 위한 인터페이스를 제공합니다.

그림 1: 바닥글 행은 새 제품을 추가하기 위한 인터페이스를 제공합니다(전체 크기 이미지를 보려면 클릭).

1단계: GridView에 제품 정보 표시

GridView 바닥글에 삽입 인터페이스를 만들기 전에 먼저 데이터베이스의 제품을 나열하는 페이지에 GridView를 추가하는 데 집중하겠습니다. 먼저 폴더에서 InsertThroughFooter.aspxEnhancedGridView 페이지를 열고 Toolbox에서 Designer GridView를 끌어 GridView의 ID 속성을 로 Products설정합니다. 다음으로 GridView의 스마트 태그를 사용하여 이라는 ProductsDataSource새 ObjectDataSource에 바인딩합니다.

ProductsDataSource라는 새 ObjectDataSource 만들기

그림 2: 새 ObjectDataSource 이름 ProductsDataSource 만들기(전체 크기 이미지를 보려면 클릭)

클래스의 GetProducts() 메서드를 사용하여 ProductsBLL 제품 정보를 검색하도록 ObjectDataSource를 구성합니다. 이 자습서에서는 삽입 기능을 추가하는 데 중점을 두고 편집 및 삭제에 대해 걱정하지 않도록 하겠습니다. 따라서 INSERT 탭의 드롭다운 목록이 로 설정 AddProduct() 되고 UPDATE 및 DELETE 탭의 드롭다운 목록이 (없음) 로 설정되어 있는지 확인합니다.

AddProduct 메서드를 ObjectDataSource의 Insert() 메서드에 매핑

그림 3: 메서드를 AddProduct ObjectDataSource의 Insert() 메서드에 매핑(전체 크기 이미지를 보려면 클릭)

UPDATE 및 DELETE 탭 Drop-Down Lists (없음)으로 설정

그림 4: 업데이트 및 삭제 탭 Drop-Down Lists (없음)으로 설정(전체 크기 이미지를 보려면 클릭)

ObjectDataSource의 데이터 원본 구성 마법사를 완료하면 Visual Studio에서 해당 데이터 필드 각각에 대한 필드를 GridView에 자동으로 추가합니다. 지금은 Visual Studio에서 추가한 모든 필드를 그대로 둡니다. 이 자습서의 뒷부분에서 새 레코드를 추가할 때 값을 지정할 필요가 없는 일부 필드를 제거합니다.

데이터베이스에 80개에 가까운 제품이 있으므로 새 레코드를 추가하려면 사용자가 웹 페이지 아래쪽까지 스크롤해야 합니다. 따라서 페이징을 사용하도록 설정하여 삽입 인터페이스를 더 잘 표시하고 액세스할 수 있도록 합니다. 페이징을 켜려면 GridView의 스마트 태그에서 페이징 사용 확인란을 검사.

이 시점에서 GridView 및 ObjectDataSource의 선언적 태그는 다음과 유사하게 표시됩니다.

<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>

모든 제품 데이터 필드가 Paged GridView에 표시됩니다.

그림 5: 모든 제품 데이터 필드가 Paged GridView에 표시됩니다(전체 크기 이미지를 보려면 클릭).

GridView에는 머리글 및 데이터 행과 함께 바닥글 행이 포함됩니다. 머리글 및 바닥글 행은 GridView ShowHeaderShowFooter 속성의 값에 따라 표시됩니다. 바닥글 행을 표시하려면 속성을 로 설정 ShowFooter 하기만 하면됩니다 True. 그림 6에서 알 수 있듯이 속성을 로 설정 ShowFooter 하면 True 표에 바닥글 행이 추가됩니다.

바닥글 행을 표시하려면 ShowFooter를 True로 설정합니다.

그림 6: 바닥글 행을 표시하려면 를 로 True 설정합니다ShowFooter(전체 크기 이미지를 보려면 클릭).

바닥글 행에는 진한 빨간색 배경색이 있습니다. 이는 ObjectDataSource를 사용하여 데이터 표시 자습서에서 만들고 모든 페이지에 다시 적용한 DataWebControls 테마 때문입니다. 특히 파일은 GridView.skin CSS 클래스를 FooterStyle 사용하는 FooterStyle 속성을 구성합니다. 클래스는 FooterStyle 다음과 같이 에서 Styles.css 정의됩니다.

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

참고

이전 자습서에서 GridView의 바닥글 행을 사용하여 살펴보았습니다. 필요한 경우 GridView의 바닥글에 요약 정보 표시 자습서를 다시 참조하여 새로 고침을 참조하세요.

속성을 TrueShowFooter 설정한 후 잠시 시간을 내어 브라우저에서 출력을 봅니다. 현재 바닥글 행에는 텍스트 또는 웹 컨트롤이 없습니다. 3단계에서는 적절한 삽입 인터페이스를 포함할 수 있도록 각 GridView 필드의 바닥글을 수정합니다.

빈 바닥글 행이 페이징 인터페이스 컨트롤 위에 표시됩니다.

그림 7: 빈 바닥글 행이 페이징 인터페이스 컨트롤 위에 표시됩니다(전체 크기 이미지를 보려면 클릭).

GridView 컨트롤 자습서의 TemplateFields 사용 자습서에서는 TemplateFields(BoundFields 또는 CheckBoxFields와 반대)를 사용하여 특정 GridView 열의 표시를 크게 사용자 지정하는 방법을 알아보았습니다. 데이터 수정 인터페이스 사용자 지정에서 TemplateFields를 사용하여 GridView에서 편집 인터페이스를 사용자 지정하는 방법을 살펴보았습니다. TemplateField는 특정 유형의 행에 사용되는 태그, 웹 컨트롤 및 데이터 바인딩 구문의 혼합을 정의하는 여러 템플릿으로 구성됩니다. 예를 들어 는 ItemTemplate읽기 전용 행에 사용되는 템플릿을 지정하고 EditItemTemplate 는 편집 가능한 행에 대한 템플릿을 정의합니다.

EditItemTemplate와 함께 ItemTemplate TemplateField에는 바닥글 행의 콘텐츠를 지정하는 도 포함 FooterTemplate 됩니다. 따라서 각 필드의 삽입 인터페이스에 필요한 웹 컨트롤을 에 FooterTemplate추가할 수 있습니다. 시작하려면 GridView의 모든 필드를 TemplateFields로 변환합니다. GridView의 스마트 태그에서 열 편집 링크를 클릭하고, 왼쪽 아래 모서리에 있는 각 필드를 선택하고, 이 필드를 TemplateField로 변환 링크를 클릭하여 이 작업을 수행할 수 있습니다.

각 필드를 TemplateField로 변환

그림 8: 각 필드를 TemplateField로 변환

이 필드를 TemplateField로 변환을 클릭하면 현재 필드 형식이 해당하는 TemplateField로 바뀝니다. 예를 들어 각 BoundField는 해당 데이터 필드를 표시하는 레이블과 ItemTemplateEditItemTemplate TextBox에 데이터 필드를 표시하는 가 포함된 으로 TemplateField로 바뀝니다. ProductName BoundField가 다음 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>

마찬가지로 Discontinued CheckBoxField는 및 에 CheckBox 웹 컨트롤이 포함된 TemplateField ItemTemplateEditItemTemplate 로 변환되었습니다(s CheckBox가 ItemTemplate 비활성화됨). 읽기 전용 ProductID BoundField는 및 EditItemTemplate둘 다 ItemTemplate 에 레이블 컨트롤이 있는 TemplateField로 변환되었습니다. 즉, 기존 GridView 필드를 TemplateField로 변환하는 것은 기존 필드의 기능을 잃지 않고 더 사용자 지정 가능한 TemplateField로 전환하는 빠르고 쉬운 방법입니다.

작업 중인 GridView는 편집을 지원하지 않으므로 각 TemplateField에서 를 자유롭게 제거 EditItemTemplate 하여 을 그대로 유지합니다 ItemTemplate. 이렇게 하면 GridView의 선언적 태그가 다음과 같이 표시됩니다.

<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>

이제 각 GridView 필드가 TemplateField로 변환되었으므로 각 필드의 FooterTemplate에 적절한 삽입 인터페이스를 입력할 수 있습니다. 일부 필드에는 삽입 인터페이스가 없습니다(ProductIDinstance 경우). 다른 필드에는 새 제품 정보를 수집하는 데 사용되는 웹 컨트롤이 다릅니다.

편집 인터페이스를 만들려면 GridView의 스마트 태그에서 템플릿 편집 링크를 선택합니다. 그런 다음 드롭다운 목록에서 적절한 필드 s FooterTemplate 를 선택하고 도구 상자에서 Designer 적절한 컨트롤을 끕니다.

각 필드의 FooterTemplate에 적절한 삽입 인터페이스 추가

그림 9: 각 필드에 FooterTemplate 적절한 삽입 인터페이스 추가(전체 크기 이미지를 보려면 클릭)

다음 글머리 기호 목록은 추가할 삽입 인터페이스를 지정하여 GridView 필드를 열거합니다.

  • ProductID 없음.
  • ProductName TextBox를 추가하고 를 IDNewProductName설정합니다. RequiredFieldValidator 컨트롤을 추가하여 사용자가 새 제품 이름에 대한 값을 입력하도록 합니다.
  • SupplierID 없음.
  • CategoryID 없음.
  • QuantityPerUnit TextBox를 추가하고 를 IDNewQuantityPerUnit설정합니다.
  • UnitPrice 입력한 값이 0보다 크거나 같은 통화 값인지 확인하는 CompareValidator라는 NewUnitPrice TextBox를 추가합니다.
  • UnitsInStock 로 설정된 TextBox IDNewUnitsInStock사용합니다. 입력한 값이 0보다 크거나 같은 정수 값인지 확인하는 CompareValidator를 포함합니다.
  • UnitsOnOrder 로 설정된 TextBox IDNewUnitsOnOrder사용합니다. 입력한 값이 0보다 크거나 같은 정수 값인지 확인하는 CompareValidator를 포함합니다.
  • ReorderLevel 로 설정된 TextBox IDNewReorderLevel사용합니다. 입력한 값이 0보다 크거나 같은 정수 값인지 확인하는 CompareValidator를 포함합니다.
  • Discontinued CheckBox를 추가하고 를 IDNewDiscontinued설정합니다.
  • CategoryName DropDownList를 추가하고 를 IDNewCategoryID설정합니다. 라는 CategoriesDataSource 새 ObjectDataSource에 바인딩하고 클래스의 GetCategories() 메서드를 CategoriesBLL 사용하도록 구성합니다. DropDownList가 ListItem 데이터 필드를 값으로 사용하여 CategoryID 데이터 필드를 표시 CategoryName 하게 합니다.
  • SupplierName DropDownList를 추가하고 를 IDNewSupplierID설정합니다. 라는 SuppliersDataSource 새 ObjectDataSource에 바인딩하고 클래스의 GetSuppliers() 메서드를 SuppliersBLL 사용하도록 구성합니다. DropDownList가 ListItem 데이터 필드를 값으로 사용하여 SupplierID 데이터 필드를 표시 CompanyName 하게 합니다.

각 유효성 검사 컨트롤에 대해 CSS 클래스의 흰색 전경색이 FooterStyle 기본 빨간색 대신 사용되도록 속성을 지웁 ForeColor 니다. 자세한 설명을 위해 속성을 사용 ErrorMessage 하지만 속성을 별표로 설정합니다 Text . 삽입 인터페이스가 두 줄로 줄 바꿈되지 않도록 유효성 검사 컨트롤의 텍스트를 방지하려면 유효성 검사 컨트롤을 사용하는 각 FooterTemplate 에 대해 s Wrap 속성을 false로 설정합니다FooterStyle. 마지막으로 GridView 아래에 ValidationSummary 컨트롤을 추가하고 해당 속성을 로 설정하고 해당 ShowMessageBoxShowSummary 속성을 TrueFalse설정합니다.

새 제품을 추가할 때 및 SupplierIDCategoryID 제공해야 합니다. 이 정보는 및 SupplierName 필드의 바닥글 셀에 있는 DropDownLists를 CategoryName 통해 캡처됩니다. 그리드의 데이터 행에서 사용자가 ID 값이 아닌 범주 및 공급자 이름을 보는 데 더 관심이 있기 때문에 및 SupplierID TemplateFields와 달리 CategoryID 이러한 필드를 사용하기로 결정했습니다. CategoryID 이제 및 SupplierID 값이 및 SupplierName 필드의 삽입 인터페이스에서 CategoryName 캡처되므로 GridView에서 및 SupplierID TemplateFields를 제거할 CategoryID 수 있습니다.

마찬가지로 ProductID 은 새 제품을 추가할 때 사용되지 않으므로 ProductID TemplateField도 제거할 수 있습니다. 그러나 필드를 그리드에 ProductID 남겨 두겠습니다. 삽입 인터페이스를 구성하는 TextBoxes, DropDownLists, CheckBoxes 및 유효성 검사 컨트롤 외에도 클릭할 때 새 제품을 데이터베이스에 추가하는 논리를 수행하는 추가 단추도 필요합니다. 4단계에서는 TemplateField의 FooterTemplate삽입 인터페이스 ProductID 에 추가 단추를 포함합니다.

다양한 GridView 필드의 모양을 자유롭게 개선할 수 있습니다. 예를 들어 값의 형식을 UnitPrice 통화로 지정하고, , UnitsOnOrder및 필드를 마우스 오른쪽 단추로 정렬UnitsInStock하고ReorderLevel, TemplateFields의 HeaderText 값을 업데이트할 수 있습니다.

에 인터페이스 FooterTemplate 를 삽입하고, , 및 TemplateFields를 SupplierID제거하고, TemplateFields의 서식을 지정하고 CategoryID 정렬하여 그리드의 미학을 개선한 후 GridView의 선언적 태그는 다음과 유사하게 표시됩니다.

<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>

브라우저를 통해 볼 때 GridView의 바닥글 행에 완료된 삽입 인터페이스가 포함됩니다(그림 10 참조). 이 시점에서 삽입 인터페이스에는 사용자가 새 제품에 대한 데이터를 입력하고 데이터베이스에 새 레코드를 삽입하려고 함을 나타내는 수단이 포함되어 있지 않습니다. 또한 바닥글에 입력된 데이터가 데이터베이스의 새 레코드 Products 로 변환되는 방법을 아직 다루지 않았습니다. 4단계에서는 삽입 인터페이스에 추가 단추를 포함하는 방법과 클릭 시 포스트백에서 코드를 실행하는 방법을 살펴보겠습니다. 5단계는 바닥글의 데이터를 사용하여 새 레코드를 삽입하는 방법을 보여줍니다.

GridView 바닥글은 새 레코드를 추가하기 위한 인터페이스를 제공합니다.

그림 10: GridView 바닥글은 새 레코드를 추가하기 위한 인터페이스를 제공합니다(전체 크기 이미지를 보려면 클릭).

4단계: 삽입 인터페이스에 추가 단추 포함

바닥글 행의 삽입 인터페이스에 현재 사용자가 새 제품 정보 입력을 완료했음을 나타낼 수 있는 수단이 없기 때문에 삽입 인터페이스의 어딘가에 추가 단추를 포함해야 합니다. 기존 FooterTemplate 중 하나에 배치하거나 이 목적을 위해 표에 새 열을 추가할 수 있습니다. 이 자습서에서는 TemplateField의 FooterTemplateProductID 추가 단추를 배치해 보겠습니다.

Designer GridView의 스마트 태그에서 템플릿 편집 링크를 클릭한 다음 드롭다운 목록에서 필드 s FooterTemplate 를 선택합니다ProductID. 서식 파일에 단추 웹 컨트롤(또는 원하는 경우 LinkButton 또는 ImageButton)을 추가하고, ID를 AddProduct삽입 CommandName 으로 설정하고, 해당 Text 속성을 그림 11에 표시된 대로 추가로 설정합니다.

ProductID TemplateField의 FooterTemplate에 추가 단추 배치

그림 11: 템플릿 필드에 FooterTemplate 추가 단추 ProductID 배치(전체 크기 이미지를 보려면 클릭)

추가 단추를 포함했으면 브라우저에서 페이지를 테스트합니다. 삽입 인터페이스에서 잘못된 데이터가 있는 추가 단추를 클릭하면 포스트백이 단락되고 ValidationSummary 컨트롤이 잘못된 데이터를 나타냅니다(그림 12 참조). 적절한 데이터를 입력하면 추가 단추를 클릭하면 포스트백이 발생합니다. 그러나 데이터베이스에 레코드가 추가되지 않습니다. 실제로 삽입을 수행하려면 약간의 코드를 작성해야 합니다.

삽입 인터페이스에 잘못된 데이터가 있는 경우 추가 단추의 포스트백이 단락됩니다.

그림 12: 삽입 인터페이스에 잘못된 데이터가 있는 경우 단추 추가 포스트백이 단락됩니다(전체 크기 이미지를 보려면 클릭).

참고

삽입 인터페이스의 유효성 검사 컨트롤이 유효성 검사 그룹에 할당되지 않았습니다. 삽입 인터페이스가 페이지의 유일한 유효성 검사 컨트롤 집합인 한 이 작업은 정상적으로 작동합니다. 그러나 페이지에 다른 유효성 검사 컨트롤(예: 그리드 편집 인터페이스의 유효성 검사 컨트롤)이 있는 경우 삽입 인터페이스 및 추가 단추 속성의 ValidationGroup 유효성 검사 컨트롤은 이러한 컨트롤을 특정 유효성 검사 그룹과 연결하기 위해 동일한 값을 할당해야 합니다. 유효성 검사 컨트롤 및 페이지의 단추를 유효성 검사 그룹으로 분할하는 방법에 대한 자세한 내용은 ASP.NET 2.0 의 유효성 검사 컨트롤 해부를 참조하세요.

5단계: 테이블에 새 레코드Products삽입

GridView의 기본 제공 편집 기능을 활용할 때 GridView는 업데이트를 수행하는 데 필요한 모든 작업을 자동으로 처리합니다. 특히 업데이트 단추를 클릭하면 편집 인터페이스에서 입력한 값을 ObjectDataSource 컬렉션의 UpdateParameters 매개 변수에 복사하고 ObjectDataSource 메서드 Update() 를 호출하여 업데이트를 시작합니다. GridView는 삽입을 위한 이러한 기본 제공 기능을 제공하지 않으므로 ObjectDataSource 메서드를 호출하고 삽입 인터페이스의 Insert() 값을 ObjectDataSource 컬렉션 InsertParameters 에 복사하는 코드를 구현해야 합니다.

이 삽입 논리는 추가 단추를 클릭한 후에 실행해야 합니다. GridView의 단추 추가 및 응답 자습서에서 설명한 것처럼 GridView에서 Button, LinkButton 또는 ImageButton을 클릭하면 GridView 이벤트가 RowCommand 포스트백에서 발생합니다. 이 이벤트는 단추, LinkButton 또는 ImageButton이 바닥글 행의 추가 단추와 같이 명시적으로 추가되었는지 또는 GridView에 의해 자동으로 추가되었는지 여부(예: 정렬 사용이 선택되었을 때 각 열의 맨 위에 있는 LinkButtons 또는 페이징 사용이 선택되었을 때 페이징 인터페이스의 LinkButtons)가 발생합니다.

따라서 추가 단추를 클릭하는 사용자에게 응답하려면 GridView RowCommand 이벤트에 대한 이벤트 처리기를 만들어야 합니다. 이 이벤트는 GridView의 Button, LinkButton 또는 ImageButton을 클릭 때마다 발생하므로 이벤트 처리기에 전달된 속성이 추가 단추 값(삽입)에 매핑되는 경우에만 CommandName 삽입 논리를 진행해야 CommandName 합니다. 또한 유효성 검사 컨트롤이 유효한 데이터를 보고하는 경우에만 진행해야 합니다. 이를 수용하려면 다음 코드를 사용하여 이벤트에 대한 RowCommand 이벤트 처리기를 만듭니다.

Protected Sub Products_RowCommand(sender As Object, e As GridViewCommandEventArgs) _
    Handles Products.RowCommand
    
    ' Insert data if the CommandName == "Insert" 
    ' and the validation controls indicate valid data...
    If e.CommandName = "Insert" AndAlso Page.IsValid Then
        ' TODO: Insert new record...
    End If
End Sub

참고

이벤트 처리기가 속성을 확인하는 데 방해가 되는 이유가 궁금할 Page.IsValid 수 있습니다. 결국 삽입 인터페이스에 잘못된 데이터가 제공되면 포스트백이 표시되지 않나요? 사용자가 JavaScript를 사용하지 않도록 설정하지 않았거나 클라이언트 쪽 유효성 검사 논리를 우회하는 단계를 수행한 경우 이 가정은 정확합니다. 즉, 클라이언트 쪽 유효성 검사에 엄격하게 의존해서는 안 됩니다. 유효성을 위한 서버 쪽 검사 데이터를 사용하기 전에 항상 수행해야 합니다.

1단계에서는 해당 Insert() 메서드가 클래스의 AddProduct 메서드에 ProductsDataSource 매핑되도록 ObjectDataSource를 ProductsBLL 만들었습니다. 테이블에 새 레코드를 Products 삽입하려면 ObjectDataSource의 Insert() 메서드를 호출하기만 하면됩니다.

Protected Sub Products_RowCommand(sender As Object, e As GridViewCommandEventArgs) _
    Handles Products.RowCommand
    
    ' Insert data if the CommandName == "Insert" 
    ' and the validation controls indicate valid data...
    If e.CommandName = "Insert" AndAlso Page.IsValid Then
        ' Insert new record
        ProductsDataSource.Insert()
    End If
End Sub

이제 메서드가 Insert() 호출되었으므로 남은 것은 삽입 인터페이스의 값을 클래스의 AddProduct 메서드에 전달된 매개 변수로 복사하는 것입니다ProductsBLL. 삽입, 업데이트 및 삭제와 관련된 이벤트 검사 자습서에서 살펴본 것처럼 ObjectDataSource의 Inserting 이벤트를 통해 이 작업을 수행할 수 있습니다. Inserting 이 경우 GridView 바닥글 행의 컨트롤을 Products 프로그래밍 방식으로 참조하고 해당 값을 컬렉션에 e.InputParameters 할당해야 합니다. 사용자가 TextBox를 비워 두 ReorderLevel 는 등의 값을 생략하는 경우 데이터베이스에 삽입된 값이 이어야 함을 지정해야 합니다 NULL. 메서드는 AddProducts nullable 데이터베이스 필드에 대해 nullable 형식을 허용하므로 nullable 형식을 사용하고 사용자 입력을 생략하는 Nothing 경우 값을 로 설정합니다.

Protected Sub ProductsDataSource_Inserting _
    (sender As Object, e As .ObjectDataSourceMethodEventArgs) _
    Handles ProductsDataSource.Inserting
    
    ' Programmatically reference Web controls in the inserting interface...
    Dim NewProductName As TextBox = _
        Products.FooterRow.FindControl("NewProductName")
    Dim NewCategoryID As DropDownList = _
        Products.FooterRow.FindControl("NewCategoryID")
    Dim NewSupplierID As DropDownList = _
        Products.FooterRow.FindControl("NewSupplierID")
    Dim NewQuantityPerUnit As TextBox = _
        Products.FooterRow.FindControl("NewQuantityPerUnit")
    Dim NewUnitPrice As TextBox = _
        Products.FooterRow.FindControl("NewUnitPrice")
    Dim NewUnitsInStock As TextBox = _
        Products.FooterRow.FindControl("NewUnitsInStock")
    Dim NewUnitsOnOrder As TextBox = _
        Products.FooterRow.FindControl("NewUnitsOnOrder")
    Dim NewReorderLevel As TextBox = _
        Products.FooterRow.FindControl("NewReorderLevel")
    Dim NewDiscontinued As 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)
    Dim quantityPerUnit As String = Nothing
    If Not String.IsNullOrEmpty(NewQuantityPerUnit.Text) Then
        quantityPerUnit = NewQuantityPerUnit.Text
    End If
    e.InputParameters("quantityPerUnit") = quantityPerUnit
    Dim unitPrice As Nullable(Of Decimal) = Nothing
    If Not String.IsNullOrEmpty(NewUnitPrice.Text) Then
        unitPrice = Convert.ToDecimal(NewUnitPrice.Text)
    End If
    e.InputParameters("unitPrice") = unitPrice
    Dim unitsInStock As Nullable(Of Short) = Nothing
    If Not String.IsNullOrEmpty(NewUnitsInStock.Text) Then
        unitsInStock = Convert.ToInt16(NewUnitsInStock.Text)
    End If
    e.InputParameters("unitsInStock") = unitsInStock
    Dim unitsOnOrder As Nullable(Of Short) = Nothing
    If Not String.IsNullOrEmpty(NewUnitsOnOrder.Text) Then
        unitsOnOrder = Convert.ToInt16(NewUnitsOnOrder.Text)
    End If
    e.InputParameters("unitsOnOrder") = unitsOnOrder
    Dim reorderLevel As Nullable(Of Short) = Nothing
    If Not String.IsNullOrEmpty(NewReorderLevel.Text) Then
        reorderLevel = Convert.ToInt16(NewReorderLevel.Text)
    End If
    e.InputParameters("reorderLevel") = reorderLevel
    e.InputParameters("discontinued") = NewDiscontinued.Checked
End Sub

Inserting 이벤트 처리기가 완료되면 GridView의 바닥글 행을 Products 통해 데이터베이스 테이블에 새 레코드를 추가할 수 있습니다. 계속해서 몇 가지 새로운 제품을 추가해 보세요.

추가 작업 향상 및 사용자 지정

현재 추가 단추를 클릭하면 데이터베이스 테이블에 새 레코드가 추가되지만 레코드가 성공적으로 추가되었다는 시각적 피드백은 제공하지 않습니다. 이상적으로 레이블 웹 컨트롤 또는 클라이언트 쪽 경고 상자는 삽입이 성공적으로 완료되었음을 사용자에게 알릴 수 있습니다. 나는 이것을 독자를위한 운동으로 남겨 둡니다.

이 자습서에 사용된 GridView는 나열된 제품에 정렬 순서를 적용하지 않으며 최종 사용자가 데이터를 정렬하도록 허용하지 않습니다. 따라서 레코드는 기본 키 필드에 의해 데이터베이스에 있는 대로 순서가 지정됩니다. 새 레코드마다 마지막 레코드 ProductID 보다 큰 값이 있으므로 새 제품이 추가될 때마다 그리드 끝에 압정됩니다. 따라서 새 레코드를 추가한 후 사용자를 GridView의 마지막 페이지로 자동으로 보낼 수 있습니다. 이 작업은 GridView에 데이터를 바인딩한 후 사용자가 마지막 페이지로 ProductsDataSource.Insert() 전송되어야 함을 나타내기 위해 이벤트 처리기에서 RowCommand 를 호출한 후 다음 코드 줄을 추가하여 수행할 수 있습니다.

' Indicate that the user needs to be sent to the last page
SendUserToLastPage = True

SendUserToLastPage 는 처음에 값 False이 할당된 페이지 수준 부울 변수입니다. GridView의 DataBound 이벤트 처리기에서 가 false PageIndex 이면 SendUserToLastPage 속성을 업데이트하여 사용자를 마지막 페이지로 보냅니다.

Protected Sub Products_DataBound(sender As Object, e As EventArgs) _
    Handles Products.DataBound
    
    ' Send user to last page of data, if needed
    If SendUserToLastPage Then
        Products.PageIndex = Products.PageCount - 1
    End If
End Sub

속성이 PageIndex 이벤트 처리기와 달리 RowCommand 이벤트 처리기에서 DataBound 설정되는 이유는 이벤트 처리기가 실행될 때 RowCommand 데이터베이스 테이블에 새 레코드 Products 를 아직 추가하지 않았기 때문입니다. 따라서 이벤트 처리기 RowCommand 에서 마지막 페이지 인덱스(PageCount - 1)는 새 제품이 추가되기 의 마지막 페이지 인덱스를 나타냅니다. 추가되는 대부분의 제품의 경우 새 제품을 추가한 후 마지막 페이지 인덱스가 동일합니다. 그러나 추가된 제품이 새 마지막 페이지 인덱스가 생성되면 이벤트 처리기에서 RowCommand 를 잘못 업데이트 PageIndex 하면 새 마지막 페이지 인덱스가 아닌 두 번째 페이지에서 마지막 페이지(새 제품을 추가하기 전의 마지막 페이지 인덱스)로 이동합니다. DataBound 이벤트 처리기는 새 제품이 추가되고 데이터가 그리드로 반등한 후에 발생하므로 속성을 설정 PageIndex 하여 올바른 마지막 페이지 인덱스가 표시되는 것을 알 수 있습니다.

마지막으로 이 자습서에 사용된 GridView는 새 제품을 추가하기 위해 수집해야 하는 필드의 수로 인해 매우 넓습니다. 이 너비로 인해 DetailsView의 세로 레이아웃이 선호될 수 있습니다. 더 적은 수의 입력을 수집하여 GridView의 전체 너비를 줄일 수 있습니다. 새 제품을 추가할 때 , UnitsInStockReorderLevel 필드를 수집UnitsOnOrder할 필요가 없으므로 이 경우 GridView에서 이러한 필드를 제거할 수 있습니다.

수집된 데이터를 조정하기 위해 다음 두 가지 방법 중 하나를 사용할 수 있습니다.

  • , 및 ReorderLevel 필드에 대한 값이 필요한 메서드를 UnitsOnOrder계속 사용합니다AddProduct. UnitsInStock Inserting 이벤트 처리기에서 삽입 인터페이스에서 제거된 이러한 입력에 사용할 하드 코딩된 기본값을 제공합니다.
  • , UnitsInStock및 필드에 대한 입력을 AddProductProductsBLL 허용하지 않는 클래스에서 메서드의 새 오버로드를 UnitsOnOrder만듭니 ReorderLevel 다. 그런 다음, ASP.NET 페이지에서 이 새 오버로드를 사용하도록 ObjectDataSource를 구성합니다.

두 옵션 모두 동일하게 작동합니다. 이전 자습서에서는 후자 옵션을 사용하여 클래스의 UpdateProduct 메서드에 대해 여러 오버로드를 ProductsBLL 만들었습니다.

요약

GridView에는 DetailsView 및 FormView에 있는 기본 제공 삽입 기능이 없지만 약간의 노력으로 삽입 인터페이스를 바닥글 행에 추가할 수 있습니다. GridView에 바닥글 행을 표시하려면 해당 ShowFooter 속성을 로 설정하기만 하면됩니다 True. 필드를 TemplateField로 변환하고 삽입 인터페이스 FooterTemplate를 에 추가하여 각 필드에 대해 바닥글 행 콘텐츠를 사용자 지정할 수 있습니다. 이 자습서에서 살듯이 에는 단추, FooterTemplate TextBoxes, DropDownLists, CheckBoxes, 데이터 기반 웹 컨트롤을 채우기 위한 데이터 원본 컨트롤(예: DropDownLists) 및 유효성 검사 컨트롤이 포함될 수 있습니다. 사용자 입력을 수집하기 위한 컨트롤과 함께 단추 추가, LinkButton 또는 ImageButton이 필요합니다.

추가 단추를 클릭하면 ObjectDataSource의 Insert() 메서드가 호출되어 삽입 워크플로를 시작합니다. 그런 다음 ObjectDataSource는 구성된 insert 메서드(이 자습서에서는 ProductsBLL 클래스의 AddProduct 메서드)를 호출합니다. Insert 메서드를 호출하기 전에 GridView의 삽입 인터페이스에서 ObjectDataSource 컬렉션 InsertParameters 으로 값을 복사해야 합니다. 이 작업은 ObjectDataSource의 Inserting 이벤트 처리기에서 삽입 인터페이스 웹 컨트롤을 프로그래밍 방식으로 참조하여 수행할 수 있습니다.

이 자습서에서는 GridView의 모양을 향상시키기 위한 기술을 살펴봅니다. 다음 자습서 집합에서는 이미지, PDF, Word 문서 등과 같은 이진 데이터 및 데이터 웹 컨트롤을 사용하는 방법을 살펴봅니다.

행복한 프로그래밍!

저자 정보

7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술로 작업해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 티치 유어셀프 ASP.NET 24시간 만에 2.0입니다. 그는 에서mitchell@4GuysFromRolla.com 또는 에서 찾을 http://ScottOnWriting.NET수있는 자신의 블로그를 통해 도달 할 수 있습니다.

특별 감사

이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 버나데트 리였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 에 줄을 놓습니다 mitchell@4GuysFromRolla.com.