Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
oleh Scott Mitchell
Dalam tutorial ini kita akan menjelajahi cara menggunakan Repeater yang bersarang di dalam Repeater lainnya. Contoh akan menggambarkan cara mengisi Repeater bagian dalam baik secara deklaratif maupun terprogram.
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) di dalam template-nya. Tantangan dengan antarmuka seperti itu adalah mengaitkan 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 lainnya. Pengulang luar akan berisi item untuk setiap kategori dalam database, menampilkan nama dan deskripsi kategori. Setiap item kategori dalam Repeater akan menampilkan informasi untuk setiap produk milik kategori tersebut (lihat Gambar 1) dalam daftar berpoin. Contoh kami akan menggambarkan cara mengisi Repeater internal baik secara deklaratif maupun dengan pemrograman.
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 membahas langkah-langkah yang dibutuhkan untuk menambahkan Repeater ke halaman yang mencantumkan nama dan deskripsi untuk 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
.
Gambar 2: Beri nama ObjectDataSource CategoriesDataSource
Baru (Klik untuk melihat gambar ukuran penuh)
Konfigurasikan ObjectDataSource sehingga menarik datanya dari CategoriesBLL
metode kelas s GetCategories
.
Gambar 3: Konfigurasikan ObjectDataSource untuk Menggunakan Metode Kelas CategoriesBLL
GetCategories
(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.
Gambar 4: Setiap Nama dan Deskripsi Kategori Tercantum, Dipisahkan oleh Aturan Horizontal (Klik untuk melihat gambar ukuran penuh)
Langkah 2: Menambahkan Pengulang Produk Berlapis
Setelah daftar kategori selesai, tugas kami berikutnya adalah menambahkan Repeater ke CategoryList
s ItemTemplate
yang nantinya 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 gunakan Repeater untuk menampilkan setiap produk dalam bentuk daftar berpoin, dengan item daftar yang mencakup nama dan harga produk.
Untuk membuat Repeater ini, kita perlu memasukkan sintaks dan templat deklaratif Repeater dalam secara manual ke dalam CategoryList
ItemTemplate
. 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 bagian dalam baik secara deklaratif - melalui properti DataSourceID
dari Repeater bagian dalam atau melalui sintaks pengikatan data yang deklaratif maupun secara terprogram dengan merujuk pada Repeater bagian dalam dalam penanganan peristiwa CategoryList
dari Repeater, mengatur propertinya secara terprogram dengan ItemDataBound
, dan memanggil metodenya DataSource
. Mari kita jelajahi masing-masing pendekatan ini.
Mengakses Data Secara Deklaratif dengan Kontrol ObjectDataSource danItemDataBound
Penanganan 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 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 ke DataSourceID
milik ObjectDataSource (ID
). Selain itu, perhatikan bahwa ObjectDataSource kami memiliki elemen <asp:Parameter>
yang menetapkan nilai categoryID
untuk 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 peristiwa ItemDataBound
dipicu sekali untuk setiap item yang terikat ke Repeater. Oleh karena itu, setiap kali peristiwa ini diaktifkan untuk Repeater terluar, kita dapat menetapkan nilai saat ini CategoryID
ke parameter ProductsByCategoryDataSource
pada CategoryID
ObjectDataSource.
Buat penanganan aktivitas untuk CategoryList
peristiwa Repeater ItemDataBound
dengan kode berikut:
protected void CategoryList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem ||
e.Item.ItemType == ListItemType.Item)
{
// Reference the CategoriesRow object being bound to this RepeaterItem
Northwind.CategoriesRow category =
(Northwind.CategoriesRow)((System.Data.DataRowView)e.Item.DataItem).Row;
// Reference the ProductsByCategoryDataSource ObjectDataSource
ObjectDataSource ProductsByCategoryDataSource =
(ObjectDataSource)e.Item.FindControl("ProductsByCategoryDataSource");
// Set the CategoryID Parameter value
ProductsByCategoryDataSource.SelectParameters["CategoryID"].DefaultValue =
category.CategoryID.ToString();
}
}
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 baru saja terikat ke RepeaterItem
yang kini aktif. Terakhir, kami mereferensikan ObjectDataSource dalam ItemTemplate
dan menetapkan nilai parameternya CategoryID
ke CategoryID
dari RepeaterItem
saat ini.
Dengan penanganan peristiwa ini, ProductsByCategoryList
Repeater di setiap RepeaterItem
terhubung dengan produk dalam kategori RepeaterItem
. Gambar 5 menunjukkan cuplikan layar dari output yang dihasilkan.
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 untuk kategori saat ini, kita dapat mengembangkan metode di kelas code-behind halaman ASP.NET kami (atau di dalam folder App_Code
atau dalam proyek Pustaka Kelas terpisah) yang mengembalikan produk yang tepat ketika ditentukan dalam CategoryID
. Bayangkan bahwa kita memiliki metode seperti itu di halaman ASP.NET kita pada kelas code-behind dan diberi nama GetProductsInCategory(categoryID)
. Dengan metode ini, kita dapat mengikat produk untuk kategori saat ini ke Repeater bagian dalam menggunakan sintaks deklaratif berikut:
<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
DataSource='<%# GetProductsInCategory((int)(Eval("CategoryID"))) %>'>
...
</asp:Repeater>
Properti Repeater s DataSource
menggunakan sintaks pengikatan data untuk menunjukkan bahwa datanya berasal dari GetProductsInCategory(categoryID)
metode . Karena Eval("CategoryID")
mengembalikan nilai tipe Object
, kita mengubah objek menjadi Integer
terlebih dahulu sebelum meneruskannya ke metode GetProductsInCategory(categoryID)
. Perhatikan bahwa CategoryID
yang diakses di sini melalui sintaks pengikatan data adalah CategoryID
dalam Repeater luar (CategoryList
), yang terikat ke rekaman pada 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 yang diberikan oleh categoryID
. Kita dapat melakukan ini dengan mengembalikan ProductsDataTable
yang dikembalikan oleh metode ProductsBLL
dari kelas GetProductsByCategoryID(categoryID)
. Mari kita buat metode GetProductsInCategory(categoryID)
di kelas code-behind untuk NestedControls.aspx
halaman. Lakukan menggunakan kode berikut:
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
// Create an instance of the ProductsBLL class
ProductsBLL productAPI = new ProductsBLL();
// Return the products in the category
return productAPI.GetProductsByCategoryID(categoryID);
}
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 sibuk untuk membuat metode GetProductsInCategory(categoryID)
di kelas code-behind halaman ASP.NET. 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((int)(Eval("CategoryID"))) %>'
? 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 metode GetProductsInCategory(categoryID)
di kelas code-behind halaman ASP.NET, metode di kelas code-behind tersebut memberikan kita lebih banyak fleksibilitas dalam bekerja dengan data yang diambil, seperti yang akan kita lihat sebentar lagi.
Mengakses Semua Informasi Produk Sekaligus
Dua teknik sebelumnya yang telah kami periksa mengambil produk tersebut untuk kategori saat ini dengan melakukan panggilan ke kelas ProductsBLL
metode GetProductsByCategoryID(categoryID)
(pendekatan pertama melakukannya melalui ObjectDataSource, yang kedua melalui metode GetProductsInCategory(categoryID)
di kelas code-behind). Setiap kali metode ini dipanggil, Lapisan Logika Bisnis memanggil Lapisan Akses Data, yang melakukan kueri SQL ke database untuk mengembalikan baris dari tabel Products
yang nilai bidang CategoryID
-nya cocok dengan parameter input yang disediakan.
Mengingat adanya N kategori dalam sistem, pendekatan ini menghasilkan N + 1 panggilan ke database. Satu kueri database dilakukan untuk mendapatkan semua kategori, dan kemudian N panggilan untuk mendapatkan produk yang spesifik 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 sesuai dengan CategoryID
yang sedang aktif yang terhubung dengan kategori tersebut dalam Repeater-nya.
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 dari metode kelas ProductsBLL
GetProductsByCategoryID(categoryID)
secara membabi buta, kita dapat terlebih dahulu mengakses semua produk (jika belum diakses sebelumnya) dan kemudian hanya mengembalikan tampilan produk yang sudah difilter berdasarkan parameter yang diteruskan CategoryID
.
private Northwind.ProductsDataTable allProducts = null;
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
// First, see if we've yet to have accessed all of the product information
if (allProducts == null)
{
ProductsBLL productAPI = new ProductsBLL();
allProducts = productAPI.GetProducts();
}
// Return the filtered view
allProducts.DefaultView.RowFilter = "CategoryID = " + categoryID;
return allProducts;
}
Perhatikan penambahan variabel tingkat halaman, allProducts
. Ini menyimpan informasi tentang semua produk dan akan terisi ketika pertama kali metode GetProductsInCategory(categoryID)
dipanggil. 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
Orang mungkin berpikir secara intuitif bahwa mengurangi jumlah akses ke database tentu saja akan meningkatkan kinerja. Namun, ini mungkin tidak terjadi. Jika Anda memiliki sejumlah besar produk yang memiliki CategoryID
sebagai NULL
, misalnya, panggilan metode GetProducts
mengembalikan sejumlah produk yang tidak pernah ditampilkan. Selain itu, mengembalikan semua produk bisa menjadi pemborosan jika Anda hanya menampilkan subset dari kategori, yang mungkin terjadi jika Anda telah menerapkan 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 menempatkan satu kontrol data Web di dalam kontrol lainnya, secara khusus memeriksa bagaimana Pengulang luar menampilkan item untuk setiap kategori dengan Pengulang dalam yang mencantumkan produk untuk setiap kategori dalam daftar berbentuk simbol atau poin. 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 data Web luar yang terikat ke kontrol data Web dalam melalui propertinya. Teknik kedua mengakses data melalui metode di kelas code-behind halaman ASP.NET. Metode ini kemudian dapat terikat ke properti kontrol Web data internal DataSource
melalui sintaks pembindungan data.
Meskipun antarmuka pengguna tertanam yang dibahas dalam tutorial ini menggunakan Repeater yang tertanam dalam Repeater, teknik ini dapat diperluas ke kontrol web data lainnya. Anda dapat memasukkan Repeater ke dalam GridView, atau GridView ke 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.