Bagikan melalui


Kontrol Web Data Berlapis (VB)

oleh Scott Mitchell

Unduh PDF

Dalam tutorial ini, kita akan menjelajahi cara menggunakan Repeater yang bersarang di dalam Repeater lain. Contoh akan menggambarkan cara mengisi Repeater di dalam baik secara deklaratif maupun dengan kode.

Pendahuluan

Selain HTML statis dan sintaks pengikatan data, templat juga dapat menyertakan kontrol Web dan Kontrol Pengguna. Kontrol Web ini dapat memiliki properti yang ditetapkan melalui sintaks deklaratif, pengikatan data, atau dapat diakses secara terprogram dalam penanganan aktivitas sisi server yang sesuai.

Dengan menyematkan kontrol dalam templat, tampilan dan pengalaman pengguna dapat disesuaikan dan ditingkatkan. Misalnya, dalam tutorial Menggunakan TemplateFields di GridView Control , kami melihat cara mengkustomisasi tampilan GridView dengan menambahkan kontrol Kalender di TemplateField untuk menunjukkan tanggal sewa karyawan; dalam tutorial Menambahkan Kontrol Validasi ke Antarmuka Pengeditan dan Penyisipan dan Menyesuaikan Antarmuka Modifikasi Data , kami melihat cara menyesuaikan antarmuka pengeditan dan penyisipan dengan menambahkan kontrol validasi, Kotak Teks, DropDownLists, dan kontrol Web lainnya.

Templat juga dapat berisi kontrol Web data lainnya. Artinya, kita dapat memiliki DataList yang berisi DataList lain (atau Repeater atau GridView atau DetailsView, dan sebagainya) dalam template-nya. Tantangan dengan antarmuka seperti itu adalah mengikat data yang sesuai ke kontrol data web internal. Ada beberapa pendekatan berbeda yang tersedia, mulai dari opsi deklaratif menggunakan ObjectDataSource hingga yang terprogram.

Dalam tutorial ini, kita akan menjelajahi cara menggunakan Repeater yang bersarang di dalam Repeater lain. Pengulang luar akan berisi item untuk setiap kategori dalam database, menampilkan nama dan deskripsi kategori. Setiap item kategori dalam Repeater internal akan menampilkan informasi untuk setiap produk yang termasuk dalam kategori tersebut (lihat Gambar 1) dalam daftar berpoin. Contoh kami akan menggambarkan cara mengisi Repeater bagian dalam baik secara deklaratif dan juga terprogram.

Setiap Kategori, Bersama dengan Produknya, tercantum

Gambar 1: Setiap Kategori, Bersama dengan Produknya, tercantum (Klik untuk melihat gambar ukuran penuh)

Langkah 1: Membuat Daftar Kategori

Saat membuat halaman yang menggunakan kontrol Web data berlapis, saya merasa berguna untuk merancang, membuat, dan menguji kontrol Web data terluar terlebih dahulu, tanpa khawatir tentang kontrol berlapis dalam. Oleh karena itu, mari kita mulai dengan menelusuri langkah-langkah yang diperlukan untuk menambahkan Pengulang ke halaman yang mencantumkan nama dan deskripsi setiap kategori.

Mulailah dengan membuka NestedControls.aspx halaman di DataListRepeaterBasics folder dan tambahkan kontrol Pengulang ke halaman, atur propertinya ID ke CategoryList. Dari tag pintar Repeater, pilih untuk membuat ObjectDataSource baru bernama CategoriesDataSource.

Namakan ObjectDataSource yang Baru sebagai CategoriesDataSource

Gambar 2: Beri nama ObjectDataSource CategoriesDataSource Baru (Klik untuk melihat gambar ukuran penuh)

Konfigurasikan ObjectDataSource sehingga menarik datanya dari CategoriesBLL metode kelas s GetCategories .

Mengonfigurasi ObjectDataSource untuk menggunakan Kelas CategoriesBLL dengan Metode GetCategories

Gambar 3: Konfigurasikan ObjectDataSource untuk Menggunakan Metode dari Kelas CategoriesBLLGetCategories (Klik untuk melihat gambar ukuran penuh)

Untuk menentukan konten templat Repeater, kita perlu membuka tampilan Sumber dan memasukkan sintaks deklaratif secara manual. ItemTemplate Tambahkan yang menampilkan nama kategori dalam <h4> elemen dan deskripsi kategori dalam elemen paragraf (<p>). Selain itu, mari kita pisahkan setiap kategori dengan aturan horizontal (<hr>). Setelah membuat perubahan ini, halaman Anda harus berisi sintaks deklaratif untuk Repeater dan ObjectDataSource yang mirip dengan berikut ini:

<asp:Repeater ID="CategoryList" DataSourceID="CategoriesDataSource"
    EnableViewState="False" runat="server">
    <ItemTemplate>
        <h4><%# Eval("CategoryName") %></h4>
        <p><%# Eval("Description") %></p>
    </ItemTemplate>
    <SeparatorTemplate>
        <hr />
    </SeparatorTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Gambar 4 menunjukkan kemajuan kami saat dilihat melalui browser.

Setiap Nama dan Deskripsi Kategori Tercantum, Dipisahkan oleh Aturan Horizontal

Gambar 4: Setiap Nama dan Deskripsi Kategori Tercantum, Dipisahkan oleh Aturan Horizontal (Klik untuk melihat gambar ukuran penuh)

Langkah 2: Menambahkan Pengulang Produk Berlapis

Dengan daftar kategori selesai, tugas kami berikutnya adalah menambahkan Repeater ke CategoryListItemTemplate yang menampilkan informasi tentang produk yang termasuk dalam kategori yang sesuai. Ada sejumlah cara kita dapat mengambil data untuk Repeater dalam ini, dua di antaranya akan segera kita jelajahi. Untuk saat ini, mari kita buat produk Repeater dalam CategoryList Repeater s ItemTemplate. Secara khusus, mari kita minta pengulang produk menampilkan setiap produk dalam daftar berpoin dengan setiap item daftar termasuk nama dan harga produk.

Untuk membuat Repeater ini, kita perlu memasukkan sintaks dan templat deklaratif Repeater dalam secara manual ke dalam CategoryListItemTemplate. Tambahkan markup berikut dalam CategoryList Repeater s ItemTemplate:

<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
    runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong>
            (<%# Eval("UnitPrice", "{0:C}") %>)</li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>

Langkah 3: Mengikat Produk Category-Specific ke ProductsByCategoryList Repeater

Jika Anda mengunjungi halaman melalui browser pada saat ini, layar Anda akan terlihat sama seperti pada Gambar 4 karena kami belum mengikat data apa pun ke Repeater. Ada beberapa cara agar kita dapat mengambil catatan produk yang sesuai dan mengikatnya ke Repeater, beberapa lebih efisien daripada yang lain. Tantangan utama di sini adalah mendapatkan kembali produk yang sesuai untuk kategori yang ditentukan.

Data yang akan diikat ke kontrol Repeater dalam dapat diakses secara deklaratif, melalui ObjectDataSource di CategoryList Repeater s ItemTemplate, atau secara terprogram, dari halaman ASP.NET s code-behind. Demikian pula, data ini dapat terikat ke Repeater dalam baik secara deklaratif - melalui properti Repeater s DataSourceID bagian dalam atau melalui sintaks pengikatan data deklaratif atau secara terprogram dengan merujuk Repeater dalam penanganan CategoryList peristiwa Repeater ItemDataBound , secara terprogram mengatur propertinya DataSource , dan memanggil metodenya DataBind() . Mari kita jelajahi masing-masing pendekatan ini.

Mengakses Data Secara Deklaratif dengan Kontrol ObjectDataSource danItemDataBoundPenanganan Aktivitas

Karena kami telah menggunakan ObjectDataSource secara luas di seluruh seri tutorial ini, pilihan paling alami untuk mengakses data untuk contoh ini adalah tetap dengan ObjectDataSource. Kelas ProductsBLL memiliki metode GetProductsByCategoryID(categoryID) yang mengembalikan informasi tentang produk-produk yang termasuk dalam kategori yang ditentukan categoryID. Oleh karena itu, kita dapat menambahkan ObjectDataSource ke CategoryList Repeater s ItemTemplate dan mengonfigurasinya untuk mengakses datanya dari metode kelas ini.

Sayangnya, Repeater tidak mengizinkan templatnya diedit melalui tampilan Desain sehingga kita perlu menambahkan sintaks deklaratif untuk kontrol ObjectDataSource ini dengan tangan. Sintaks berikut menunjukkan CategoryList Repeater s ItemTemplate setelah menambahkan ObjectDataSource baru ini (ProductsByCategoryDataSource):

<h4><%# Eval("CategoryName") %></h4>
<p><%# Eval("Description") %></p>
<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
        DataSourceID="ProductsByCategoryDataSource" runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong> -
                sold as <%# Eval("QuantityPerUnit") %> at
                <%# Eval("UnitPrice", "{0:C}") %></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server"
           SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
   <SelectParameters>
        <asp:Parameter Name="CategoryID" Type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

Saat menggunakan pendekatan ObjectDataSource, kita perlu mengatur properti ProductsByCategoryList dari Repeater DataSourceID ke ID milik ObjectDataSource (ProductsByCategoryDataSource). Selain itu, perhatikan bahwa ObjectDataSource kami memiliki elemen <asp:Parameter> yang menentukan nilai categoryID yang akan diteruskan ke metode GetProductsByCategoryID(categoryID). Tetapi bagaimana kita menentukan nilai ini? Idealnya, kita akan dapat mengatur DefaultValue properti <asp:Parameter> elemen menggunakan sintaks pengikatan data, seperti:

<asp:Parameter Name="CategoryID" Type="Int32"
    DefaultValue='<%# Eval("CategoryID")' />

Sayangnya, sintaks pengikatan data hanya valid dalam kontrol yang memiliki DataBinding event. Kelas Parameter tidak memiliki peristiwa seperti itu dan oleh karena itu sintaks di atas ilegal dan akan mengakibatkan kesalahan runtime.

Untuk mengatur nilai ini, kita perlu membuat penanganan aktivitas untuk CategoryList peristiwa Repeater.ItemDataBound Ingat bahwa event ItemDataBound diaktifkan satu kali untuk setiap item yang terikat pada Repeater. Oleh karena itu, setiap kali peristiwa ini diaktifkan untuk Repeater luar, kita dapat menetapkan nilai saat ini CategoryID ke ProductsByCategoryDataSource parameter ObjectDataSource s CategoryID .

Buat penanganan acara untuk acara CategoryList Repeater ItemDataBound dengan kode berikut:

Protected Sub CategoryList_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) _
    Handles CategoryList.ItemDataBound
    If e.Item.ItemType = ListItemType.AlternatingItem _
        OrElse e.Item.ItemType = ListItemType.Item Then
        ' Reference the CategoriesRow object being bound to this RepeaterItem
        Dim category As Northwind.CategoriesRow = _
            CType(CType(e.Item.DataItem, System.Data.DataRowView).Row, _
                Northwind.CategoriesRow)
        ' Reference the ProductsByCategoryDataSource ObjectDataSource
        Dim ProductsByCategoryDataSource As ObjectDataSource = _
            CType(e.Item.FindControl("ProductsByCategoryDataSource"), _
                ObjectDataSource)
        ' Set the CategoryID Parameter value
        ProductsByCategoryDataSource.SelectParameters("CategoryID").DefaultValue = _
            category.CategoryID.ToString()
    End If
End Sub

Penanganan aktivitas ini dimulai dengan memastikan bahwa kita berurusan dengan item data daripada item header, footer, atau pemisah. Selanjutnya, kita merujuk instans aktual CategoriesRow yang telah terikat pada RepeaterItem saat ini. Terakhir, kami mereferensikan ObjectDataSource dalam ItemTemplate dan menetapkan nilai parameter CategoryID ke CategoryID dari RepeaterItem saat ini.

Dengan penanganan peristiwa ini, ProductsByCategoryList Repeater di masing-masing RepeaterItem terikat ke produk-produk tersebut dalam kategori RepeaterItem. Gambar 5 menunjukkan cuplikan layar dari output yang dihasilkan.

Pengulang Luar Mencantumkan Setiap Kategori; Yang Dalam Mencantumkan Produk untuk Kategori tersebut

Gambar 5: Pengulang Luar Mencantumkan Setiap Kategori; Inner One Mencantumkan Produk untuk Kategori tersebut (Klik untuk melihat gambar ukuran penuh)

Mengakses Data Produk menurut Kategori Secara Terprogram

Alih-alih menggunakan ObjectDataSource untuk mengambil produk di kategori saat ini, kita bisa membuat metode dalam kelas kode belakang halaman ASP.NET kita (atau di folder App_Code atau dalam proyek Pustaka Kelas yang terpisah) yang mengembalikan kumpulan produk yang sesuai saat melewatkan parameter CategoryID. Bayangkan jika kita memiliki metode seperti itu di kelas code-behind halaman ASP.NET kita dan bahwa metode tersebut diberi nama GetProductsInCategory(categoryID). Dengan metode ini, kita dapat mengikat produk untuk kategori saat ini ke dalam Repeater menggunakan sintaks deklaratif berikut:

<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
      DataSource='<%# GetProductsInCategory(CType(Eval("CategoryID"), Integer)) %>'>
  ...
</asp:Repeater>

Properti Repeater s DataSource menggunakan sintaks pengikatan data untuk menunjukkan bahwa datanya berasal dari GetProductsInCategory(categoryID) metode . Karena Eval("CategoryID") mengembalikan nilai dengan tipe Object, kita mengubah objek menjadi Integer sebelum meneruskannya ke metode GetProductsInCategory(categoryID). Perhatikan bahwa CategoryID yang diakses di sini melalui sintaks pengikatan data adalah CategoryID di Repeater luar (CategoryList), yang terhubung dengan catatan dalam tabel Categories. Oleh karena itu, kita tahu bahwa CategoryID tidak dapat menjadi nilai database NULL , itulah sebabnya kita dapat melemparkan Eval metode secara membabi buta tanpa memeriksa apakah kita berurusan dengan DBNull.

Dengan pendekatan ini, kita perlu membuat metode GetProductsInCategory(categoryID) dan menggunakannya untuk mengambil set produk yang sesuai berdasarkan categoryID yang diberikan. Kita dapat melakukan ini hanya dengan mengembalikan hasil dari ProductsDataTable, yang dikembalikan oleh metode ProductsBLL dalam kelas GetProductsByCategoryID(categoryID). Mari kita buat metode GetProductsInCategory(categoryID) di kelas code-behind untuk halaman NestedControls.aspx. Lakukan menggunakan kode berikut:

Protected Function GetProductsInCategory(ByVal categoryID As Integer) _
    As Northwind.ProductsDataTable
    ' Create an instance of the ProductsBLL class
    Dim productAPI As ProductsBLL = New ProductsBLL()
    ' Return the products in the category
    Return productAPI.GetProductsByCategoryID(categoryID)
End Function

Metode ini hanya membuat instans ProductsBLL metode dan mengembalikan hasil GetProductsByCategoryID(categoryID) metode . Perhatikan bahwa metode harus ditandai Public atau Protected; jika metode ditandai Private, metode tidak akan dapat diakses dari markup deklaratif halaman ASP.NET.

Setelah membuat perubahan ini untuk menggunakan teknik baru ini, luangkan waktu sejenak untuk melihat halaman melalui browser. Output harus identik dengan output saat menggunakan ObjectDataSource dan ItemDataBound pendekatan penanganan aktivitas (lihat kembali ke Gambar 5 untuk melihat cuplikan layar).

Nota

Mungkin tampak seperti pekerjaan yang tampaknya tidak produktif untuk membuat metode GetProductsInCategory(categoryID) di halaman ASP.NET kelas code-behind. Bagaimanapun, metode ini hanya membuat instans kelas ProductsBLL dan mengembalikan hasil dari metode GetProductsByCategoryID(categoryID). Mengapa tidak hanya memanggil metode ini langsung dari sintaks pengikatan data di Repeater bagian dalam, seperti: DataSource='<%# ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"), Integer)) %>'? Meskipun sintaks ini tidak akan berfungsi dengan implementasi ProductsBLL kelas kami saat ini (karena GetProductsByCategoryID(categoryID) metode ini adalah metode instans), Anda dapat memodifikasi ProductsBLL untuk menyertakan metode statis GetProductsByCategoryID(categoryID) atau meminta kelas menyertakan metode statis Instance() untuk mengembalikan instans ProductsBLL baru kelas.

Meskipun modifikasi tersebut akan menghilangkan kebutuhan akan GetProductsInCategory(categoryID) method di kelas code-behind halaman ASP.NET, metode kelas code-behind memberi kita lebih banyak fleksibilitas dalam bekerja dengan data yang diambil, seperti yang akan kita lihat sebentar lagi.

Mengambil Semua Informasi Produk Sekaligus

Dua teknik sebelumnya yang telah kami periksa mengambil produk tersebut untuk kategori saat ini dengan melakukan panggilan ke metode ProductsBLL dari kelas GetProductsByCategoryID(categoryID) (pendekatan pertama dilakukan melalui ObjectDataSource, yang kedua melalui metode GetProductsInCategory(categoryID) di kelas yang didukung kode). Setiap kali metode ini dipanggil, Lapisan Logika Bisnis memanggil pada Lapisan Akses Data, yang mengkueri database menggunakan pernyataan SQL yang mengembalikan baris dari tabel Products yang bidang CategoryID cocok dengan parameter input yang disediakan.

Mengingat kategori N dalam sistem, pendekatan ini menjaring panggilan N + 1 ke database satu kueri database untuk mendapatkan semua kategori dan kemudian panggilan N untuk mendapatkan produk khusus untuk setiap kategori. Namun, kita dapat mengambil semua data yang diperlukan hanya dalam dua panggilan database satu panggilan untuk mendapatkan semua kategori dan yang lain untuk mendapatkan semua produk. Setelah kita memiliki semua produk, kita dapat memfilter produk-produk tersebut sehingga hanya produk yang cocok dengan CategoryID saat ini yang terkait dengan pengulangan internal kategori tersebut.

Untuk menyediakan fungsionalitas ini, kita hanya perlu melakukan sedikit modifikasi pada GetProductsInCategory(categoryID) metode di kelas code-behind halaman ASP.NET kami. Daripada mengembalikan hasil ProductsBLL metode kelas GetProductsByCategoryID(categoryID) secara membabi buta, kita dapat terlebih dahulu mengakses semua produk (jika belum diakses) dan kemudian mengembalikan hanya tampilan produk yang difilter berdasarkan yang diteruskan CategoryID.

Private allProducts As Northwind.ProductsDataTable = Nothing
Protected Function GetProductsInCategory(ByVal categoryID As Integer) _
    As Northwind.ProductsDataTable
    ' First, see if we've yet to have accessed all of the product information
    If allProducts Is Nothing Then
        Dim productAPI As ProductsBLL = New ProductsBLL()
        allProducts = productAPI.GetProducts()
    End If
    ' Return the filtered view
    allProducts.DefaultView.RowFilter = "CategoryID = " & categoryID
    Return allProducts
End Function

Perhatikan penambahan variabel tingkat halaman, allProducts. Ini menyimpan informasi tentang semua produk dan akan diisi ketika GetProductsInCategory(categoryID) metode dipanggil untuk pertama kali. Setelah memastikan bahwa allProducts objek telah dibuat dan diisi, metode memfilter hasil DataTable s s sehingga hanya baris CategoryID yang cocok dengan yang ditentukan CategoryID yang dapat diakses. Pendekatan ini mengurangi berapa kali database diakses dari N + 1 menjadi dua.

Peningkatan ini tidak memperkenalkan perubahan apa pun pada markup halaman yang dirender, juga tidak mengembalikan lebih sedikit rekaman daripada pendekatan lainnya. Ini hanya mengurangi jumlah panggilan ke database.

Nota

Seseorang mungkin akan berpikir secara intuitif bahwa mengurangi jumlah akses ke database akan memastikan peningkatan performa. Namun, ini mungkin tidak terjadi. Jika Anda memiliki sejumlah besar produk yang CategoryIDNULL, misalnya, panggilan ke metode GetProducts mengembalikan sejumlah produk yang tidak pernah ditampilkan. Selain itu, mengembalikan semua produk bisa menjadi pemborosan jika Anda hanya menampilkan subset kategori, yang mungkin terjadi jika Anda telah menerapkan sistem pemanggilan halaman.

Seperti biasa, dalam hal menganalisis performa dua teknik, satu-satunya ukuran surefire adalah menjalankan pengujian terkontrol yang disesuaikan untuk skenario kasus umum aplikasi Anda.

Ringkasan

Dalam tutorial ini kita melihat cara menumpuk satu kontrol data Web dalam yang lain, secara khusus memeriksa cara membuat Repeater luar menampilkan item untuk setiap kategori dengan Repeater dalam yang mencantumkan produk untuk setiap kategori dalam daftar berpoin. Tantangan utama dalam membangun antarmuka pengguna berlapis terletak pada mengakses dan mengikat data yang benar ke kontrol Web data dalam. Ada berbagai teknik yang tersedia, dua di antaranya kami periksa dalam tutorial ini. Pendekatan pertama yang diperiksa menggunakan ObjectDataSource di kontrol Web data luar yang menghubungkan ke kontrol Web data dalam melalui propertynya ItemTemplate . Teknik kedua mengakses data melalui metode dalam kelas code-behind pada halaman ASP.NET. Metode ini kemudian dapat terikat ke properti kontrol Web internal DataSource melalui sintaks pengikatan data.

Meskipun antarmuka pengguna berlapis yang diperiksa dalam tutorial ini menggunakan Repeater yang berlapis dalam Repeater, teknik ini dapat diperluas ke kontrol Web data lainnya. Anda dapat menumpuk Repeater dalam GridView, atau GridView dalam DataList, dan sebagainya.

Selamat Pemrograman!

Tentang Penulis

Scott Mitchell, penulis tujuh buku ASP/ASP.NET dan pendiri 4GuysFromRolla.com, telah bekerja sama dengan teknologi Microsoft Web sejak 1998. Scott bekerja sebagai konsultan, pelatih, dan penulis independen. Buku terbarunya adalah Sams Teach Yourself ASP.NET 2.0 dalam 24 Jam. Dia dapat dijangkau di mitchell@4GuysFromRolla.com.

Ucapan terima kasih khusus kepada

Seri tutorial ini ditinjau oleh banyak peninjau yang bermanfaat. Peninjau utama untuk tutorial ini adalah Zack Jones dan Liz Shulok. Tertarik untuk meninjau artikel MSDN saya yang akan datang? Jika demikian, hubungi saya di mitchell@4GuysFromRolla.com.