บทความ

Creating a Pageable, Sortable DataGrid

เรตติ้ง
เขียนโดย admin เมื่อวันที่ 25 January 2008 ตอน 10:04

ใช้ได้กับ

Microsoft ASP.NET

สรุป:

เรียนรู้วิธีการสร้าง DataGrid ที่จัดเรียงเรกคอร์ดได้และ DataGrid ที่จัดเพจได้ เรียนรู้ขั้นตอนที่จำเป็นที่จะผสานเทคนิคทั้งสองชนิดไปเป็น    DataGrid    ที่จัดเรียงข้อมูลและจัดเพจได้เพียงชุดเดียว เว็บคอนโทรล DataGrid  ช่วยให้นักพัฒนา  ASP.NET สามารถนำอาข้อมูลไปแสดงผลได้อย่างรวดเร็วและง่ายดาย นอกจากแสดงผลข้อมูลแบบง่ายๆแล้ว  DataGrid  ยังยอมให้มีการจัดเรียง จัดเพจ และแก้ไขข้อมูลได้ด้วย แม้ว่าการใส่ระบบจัดเรียงข้อมูลและระบบจัดเพจลงไปใน  DataGrid  เป็นเรื่องที่ตรงไปตรงมาก็ตาม แต่การสร้าง DataGrid ที่ทั้งจัดเพจและจัดเรียงข้อมูลได้พร้อมกันจัดเป็นเรื่องที่มีความท้าทายมากกว่าปกติเล็กน้อย

คำนำ

เรื่องที่มีประโยชน์มากที่สุดอย่างหนึ่งของ   Microsoft  ASP.NET  ก็คือการนำเอาข้อมูลไปแสดงผลในเว็บเพจได้โดยง่าย  ASP.NET  มีดาต้าเว็บคอนโทรล  3  ชนิดก็คือ DataGrid, DataList และ Repeater ซึ่งแต่ละชนิดถูกออกแบบมาให้แสดงผลข้อมูลได้อย่างสมบูรณ์แบบ ดาต้าเว็บคอนโทรลที่มีการใช้งานมากที่สุดก็คือ DataGrid เนื่องจากมีชุดคุณสมบัติที่มีประโยชน์ต่างๆในตัวคอนโทรลเลย คุณเพียงแต่กำหนด    properties   เพียงสองสามตัว แล้วสร้าง ตัวจัดการเหตุการณ์สองสามอันขึ้นมา DataGrid ก็สามารถจัดเรียงข้อมูล จัดเพจ หรือแก้ไขข้อมูลได้แล้ว ถ้าหากต้องการทราบความแตกต่างของดาต้าเว็บคอนโทรลทั้ง  3  ชนิด รวมทั้งการที่ควรจะนำคอนโทรลแต่ละชนิดมาใช้เมื่อไหร่ กรุณาอ่านบทความเรื่อง   "การตัดสินใจว่าเมื่อไหร่ที่ควรจะใช้   DataGrid,   DataList  หรือ Repeater"

แม้ว่าการสร้าง  DataGrid  ที่จัดเพจได้ หรือจัดเรียงข้อมูลได้อย่างใดอย่างหนึ่งเป็นเรื่องที่ตรงไปตรงมาก็ตาม แต่การสร้าง  DataGrid ที่ทั้งจัดเพจได้และจัดเรียงข้อมูลได้จัดเป็นเรื่องที่มีความท้าทายมากกว่าเล็กน้อย ในบทความนี้เราจะเริ่มต้นจากการศึกษาว่าจะสร้าง  DataGrid ที่จัดเรียงข้อมูลได้หรือจัดเพจได้อย่างไร จากนั้นทำการศึกษาว่าจะรวมเอาคุณสมบัติทั้งสองชนิดไปเป็น  Datagrid  เพียงอันเดียวได้อย่างไร บทความสองหัวข้อแรกจะพูดถึงวิธีการเพิ่มฟังก์ชันการจัดเรียงข้อมูลและฟังก์ชันการจัดเพจไปไว้ในคอนโทรล   DataGrid  ถ้าหากคุณรู้เรื่องพวกนี้ดีอยู่แล้ว ให้คุณข้ามไปยังหัวข้อสุดท้ายได้เลย ซึ่งจะเป็นการเชื่อมคุณสมบัติทั้งสองชนิดเข้าด้วยกันใน Datagrid เพียงอันเดียว

 

การสร้าง DataGrid ที่จัดเรียงข้อมูลได้

ก่อนที่เราจะเริ่มต้นสร้าง  DataGrid ที่จัดเรียงข้อมูลได้ เราต้องการ DataGrid นี้แสดงข้อมูลแบบง่ายๆบางอย่างได้เสียก่อน ซึ่งตามจุดมุ่งหมายของบทความนี้ เราลองมาสร้าง   DataGrid   แบบง่ายๆที่แสดงข้อมูลตาราง Products ในดาต้าเบส Northwind กันดีกว่า

หมายเหตุ:  ดาต้าเบส  Northwind เป็นดาต้าเบสมาตรฐานที่ให้มาพร้อมกับผลิตภัณฑ์ดาต้าเบสชนิดต่างๆของไมโครซอฟท์ อาทิเช่น Microsoft SQL Server และ Microsoft Access เป็นต้น

ให้เราสร้างเว็บเพจ  ASP.NET  อันใหม่ขึ้นมาโดยตั้งชื่อว่า SortableDataGrid.aspx แล้วเพิ่ม DataGrid ที่มี ID  property  set  ลงไปใน  dgProducts  ขั้นต่อมากำหนด AutoGenerateColumns property ให้เป็น False   และปรับแต่ง   DataGrid   เพื่อให้มันใช้  BoundColumns  สามชุดเพื่อแสดงฟิลด์  ProductName, UnitPrice    และ    UnitsInStock   สิ่งที่เหลือที่จำเป็นต้องทำก็คือการคิวรีดาต้าเบสและเชื่อมผลลัพธ์ไปยัง DataGrid  โค้ดด้านล่างต่อไปนี้แสดง Page Load event handler และ method พิเศษที่ชื่อ BindData() ซึ่งควรจะเพิ่มเข้าไปให้คลาสที่อยู่เบื้องหลังโค้ดเพื่อให้ทุกอย่างทำงานได้

private void Page_Load(object sender, System.EventArgs e)
{
   BindData();
}

private void BindData()
{
   // Connect to the Database
   SqlConnection myConnection = new SqlConnection(connection string);

   // Retrieve the SQL query results and bind it to the Repeater
   string SQL_QUERY = "SELECT ProductName, UnitPrice, UnitsInStock " +
                      "FROM Products";
   SqlCommand myCommand = new SqlCommand(SQL_QUERY, myConnection);

   myConnection.Open();
   dgProducts.DataSource = myCommand.ExecuteReader();
   dgProducts.DataBind();
   myConnection.Close();
}

หมายเหตุ การที่จะทำให้โค้ดนี้ใช้งานได้ คุณจำเป็นต้องอิมพอร์ต System.Data.SqlClientnamespace ไปไว้ในคลาสที่อยูเบื้องหลังโค้ดเสียก่อน นอกจากนั้นคุณยังจำเป็นต้องทำการเปลี่ยนแปลง   connection  string  ใน SqlConnection  constructor  ไปเป็น  connection  string ที่เชื่อมต่อกับดาต้าเบสด้วย หลังจากที่คุณใส่โค้ดนี้ลงไปแล้ว ให้คุณทำการ  build  โซลูชันขึ้นมาแล้วทำการทดสอบเว็บเพจนี้โดยการเข้าไปในเพจดังกล่าวผ่านทางเว็บบราวเซอร์ ภาพที่   1  แสดงหน้าจอของ  SortableDataGrid.aspx  (คุณสามารถทำให้หน้าตาของ DataGrid ดูโดดเด่นกว่านี้ได้โดยง่ายโดยการใช้เครื่องมือ Auto Format ของ Microsoft Visual Studio .NET)

ภาพที่ 1: sortableDataGrid.aspx เมื่อดูจากบราวเซอร์

วิธีการทำให้   DataGrid   ใน  SortableDataGrid.aspx  จัดเรียงข้อมูลได้ คุณต้องเริ่มต้นโดยการกำหนด AllowSorting  property ของ DataGrid ให้เป็น True เสียก่อน ซึ่งหลังจากที่ property ถูกกำหนดให้เป็น True  แล้ว  DataGrid  จะแสดงผลหัวเรื่องของแต่ละคอลัมน์เป็น LinkButton ซึ่งจะส่งผลทำให้การแสดงผลหัวเรื่องอของแต่ละคอลัมน์กลายเป็นไฮเปอร์ลิงก์ ผู้ใช้สามารถระบุว่าต้องการให้คอลัมน์ใดที่  DataGrid  ทำการจัดเรียงข้อมูลให้ โดยการคลิกไปที่ไฮเปอร์ลิงก์ที่เหมาะสมในหัวเรื่องของคอลัมน์

เมื่อมีการคลิกที่ไฮเปอร์ลิงก์ของหัวเรื่องคอลัมน์ เว็บเพจ ASP.NET จะถูกโพสต์กลับไปและ Sortcommand event ของ  DataGrid  จะเริ่มทำงาน เราในฐานะของนักพัฒนาที่สร้างเว็บเพจนี้ขึ้นมา มีหน้าที่ต้องสร้างระบบจัดการเหตุการณ์ขึ้นมาตัวหนึ่งสำหรับ  SortCommand event ของ Datagrid จากนั้นเชื่อมโยง SortCommand event ของ Datagrid    กับระบบจัดการเหตุการณ์ที่เราสร้างขึ้นมา หน้าที่ของระบบจัดการเหตุการณ์ตัวนี้ก็คือการแยกแยะว่าคอลัมน์ใดที่ผู้ใช้ต้องการจัดเรียงข้อมูล จากนั้นทำการจัดเรียงข้อมูลใหม่ แล้วเชื่อมโยงข้อมูลกลับไปหา DataGrid

ก่อนที่จะเขียนโค้ดสำหรับ  Sortcommand  event handler ก่อนอื่นเราต้องเพิ่มตัวจัดการเหตุการณ์ลงไป การใช้เครื่องมืออย่าง   Visual   Studio  .NET  ทำให้ในตอนนี้ง่ายดายอย่างมาก คุณแค่คลิกไปยัง  dgProducts DataGrid  ใน  Designes  เพื่อแก้ไข properties ของมันเท่านั้น ขั้นต่อมาคลิกที่ไอคอนสายล่อฟ้าซึ่งอยู่ด้านบนสุดของช่อง  Properties เพื่อเรียกดูเหตุการณ์ของ DataGrid ให้คุณเลื่อนไปยัง SortCommand event แล้วใส่ชื่อของตัวจัดการเหตุการณ์ที่คุณต้องการให้มีส่วนเกี่ยวข้องกับเหตุการณ์นี้ลงไป  Visual  Studio .NET จะทำงานที่เหลือให้เอง ภาพที่ 2 แสดงรายชื่อเหตุการณ์ในช่อง Properties

ภาพที่ 2 การสร้างตัวจัดการเหตุการณ์สำหรับเหตุการณ์ SortCommand ของ DataGrid

อย่างที่แสดงเอาไว้ในภาพที่  2  ก็คือผมได้ตัดสินใจตั้งชื่อตัวจัดการเหตุการณ์นี้ว่า  dgProducts_Sort แต่ชื่ออื่นๆที่เหมาะสมก็ใช้ได้เหมือนกัน หลังจากที่คุณใส่ชื่อตัวจัดการเหตุการณ์ลงไปในกรอบข้อความเหตุการณ์ที่เหมาะสมแล้ว Visual  Studio .NET จะสร้าง event handler function shell ขึ้นมาโดยอัตโนมัติในคลาสเบื้องหลังโค้ด จากนั้นทำการเชื่อมโยงเหตุการณ์กับตัวจัดการเหตุการณ์

ถ้าหากคุณไม่ได้ใช้  Visual  Studio  .NET  คุณจำเป็นต้องทำขั้นตอน  2 อย่างนี้ด้วยมือ ก่อนอื่นคุณต้องเริ่มโดยการสร้างเชลล์สำหรับตัวจัดการเหตุการณ์ ซึ่งมีหน้าตาดังนี้

private void dgProducts _Sort(object source,
                              DataGridSortCommandEventArgs e)
{
  // we'll add the code here in a bit!
}

จากนั้นเชื่อมโยง  SortCommand  event  ของ DataGrid ไปหาตัวจัดการเหตุการณ์ ซึ่งคุณสามารถทำได้หนึ่งในสองแบบนี้ก็คือ

  1. ใส่  OnSortcommand  = "dgProducts_Sort" ใน declaration ของ DataGrid (นั่นก็คือการใส่องค์ประกอบดังกล่าวลงไปใน <asp:DataGrid> start tag ในส่วนของ HTML)
  2. เขียนโปรแกรมกำหนดให้เหตุการณ์ของ            DataGrid           เชื่อมโยงกับตัวจัดการเหตุการณ์ใน InitializeComponent()  method  ในคลาสที่อยู่เบื้องหลังโค้ด ถ้าหากเป็น  C# คุณสามารถเขียนซินแทกซ์ในลักษณะนี้

dgProducts.SortCommand +=
       new DataGridSortCommandEventHandler(dgProducts_Sort);

ถ้าหากเป็น Microsoft Visual Basic .NET ซินแทกซ์จะมีหน้าตาแบบนี้

AddHandler dgProducts.SortCommand, AddressOf dgProducts_Sort

หลังจากที่ตัวจัดการเหตุการณ์ถูกสร้างขึ้นมา และถูกเชื่อมโยงไปหา  SortCommand event ของ DataGrid แล้ว เราก็พร้อมที่จะใส่โค้ดสำหรับตัวจัดการเหตุการณ์ได้ หัวใจสำคัญก็คือเราต้องแยกแยะว่าผู้ใช้ต้องการจัดเรียงข้อมูลที่อยู่ในคอลัมน์ใด จากนั้นทำการคิวรีดาต้าเบสซ้ำเพื่อเรียกผลลัพธ์ให้เรียงตามลำดับที่ต้องการ เราสามารถแยกแยะว่าคอลัมน์ใดที่ผู้ใช้คลิกลงไปแล้วก่อให้เกิด  postback  โดยการตรวจสอบ  SortExpresstion property ของพารามิเตอร์ DataGridSortCommandEventAvgs ที่ส่งต่อไปให้ตัวจัดการเหตุการณ์ dgProducts_Sort

เมื่อทราบว่าคอลัมน์    DataGrid    แต่ละคอลัมน์มีค่า    SortExpression    ที่เชื่อมโยงกันอยู่ ถ้าหาก AutoGenerateColumns   property  ของ  DataGrid  ถูกกำหนดให้เป็น  True  แต่ละคอลัมน์จะกำหนดค่า SortExpression  เท่ากับชื่อของฟิลด์  DataSource ที่คอลัมน์นั้นแสดงอยู่ ถ้าหาก AutoGenerateColumns กำหนดให้เป็น  False  เราจำเป็นต้องกำหนดค่าของ  SortExpression property โดยตรง ถ้าหากเราไม่ได้กำหนด  SortExpression  property  ให้แก่แต่ละคอลัมน์แล้วละก็ หัวเรื่องของคอลัมน์จะไม่แสดงเป็นไฮเปอร์ลิงก์ จากนั้นผู้ใช้ก็จะไม่สามารถจัดเรียงข้อมูลของคอลัมน์นั้นใน  DataGrid  ได้ เพื่อเป็นการแสดงตัวอย่างวิธีกำหนดค่า เราจึงได้กำหนดค่า  SortExpression  ของ  BoundColumns  สองคอลัมน์แรกให้ดู ส่วนคุณสามารถกำหนดให้ SortExpression  property  มีค่าเป็นอะไรก็ได้ แต่คุณต้องกำหนด  SortExpression ของ BoundColumn สองคอลัมน์แรกให้มีค่าเดียวกับ  DataField  property  หลังจากทำแบบนี้เสร็จแล้ว DataGrid declaration ของคุณควรมีหน้าตาแบบนี้

<asp:DataGrid id="dgProducts" runat="server" AllowSorting="True"
                  AutoGenerateColumns="False" ...>
   <Columns>
      <asp:BoundColumn DataField="ProductName"
                HeaderText="Product Name" 
SortExpression="ProductName"></asp:BoundColumn>
      <asp:BoundColumn DataField="UnitPrice" HeaderText="Unit Price"
                DataFormatString="{0:c}"
SortExpression="UnitPrice"></asp:BoundColumn>
      <asp:BoundColumn DataField="UnitsInStock"
                HeaderText="Units In Stock" DataFormatString="{0:d}">
      </asp:BoundColumn>
   </Columns>
</asp:DataGrid>

ในตอนนี้ในตัวจัดการเหตุการณ์  dgProducts_Sort เราจำเป็นต้องกำหนดค่าของ SortExpression แล้วทำการเชื่อมโยงข้อมูลที่เรียงลำดับอย่างถูกต้องกลับไปหา   DataGrid  มีวิธีการหลายแบบที่จะทำขั้นตอนนี้ได้ แต่วิธีการที่ง่ายที่สุดก็คือการคิวรีข้อมูลทั้งหมดซ้ำโดยให้เรียงลำดับตามฟิลด์ที่กำหนดเอาไว้ใน  SortExpression  วิธีการนี้อาจไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุด แต่ตาราง  Products ยอมรับได้ เนื่องจากตารางนี้มีเรกคอร์ดแค่ 77 เรกคอร์ดเท่านั้น หรือคุณอาจจะคิวรีซ้ำโดยการแก้ไข BindData() method ให้ยอมรับสตริงพารามิเตอร์ที่เป็นชื่อของคอลัมน์ให้ทำการจัดเรียงผลลัพธ์ก็ได้ จากนั้น BindData() เวอร์ชันแก้ไขก็จะมีหน้าตาแบบนี้

private void BindData(string orderBy)
{
   // Connect to the Database
   SqlConnection myConnection = new SqlConnection(connection string);
   // Retrieve the SQL query results and bind it to the DataGrid
   string SQL_QUERY = "SELECT ProductName, UnitPrice, UnitsInStock " +
                      "FROM Products ORDER BY " + orderBy;
   SqlCommand myCommand = new SqlCommand(SQL_QUERY, myConnection);
   myConnection.Open();
   dgProducts.DataSource = myCommand.ExecuteReader();
   dgProducts.DataBind();
   myConnection.Close();
}

private void dgProducts_Sort(object source,
          System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
{
   BindData(e.SortExpression);
}

งานที่เหลือที่ต้องทำก็คือการอัพเดต  Page Load event handler เรื่องแรกเราต้องการที่จะเรียก BindData() method   เท่านั้น ตอนที่เพจยังไม่ได้โพสต์กลับไป เนื่องจากในการโพสต์กลับไปครั้งหลังๆ  dgProducts_Sort event  handler  จะเป็นตัวเรียก  BindData() เอง เรื่องที่สอง Page Load event handler ของเราใช้ BindData()  method  เวอร์ชันเก่าอยู่ ซึ่งเป็นอันที่ไม่ได้รับอินพุดพารามิเตอร์ เราจำเป็นต้องทำการอัพเดตเพื่อที่เราจะสามารถส่งชื่อฟิลด์ที่ต้องการไปให้ DataGrid ทำการจัดเรียงได้

private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack)
BindData("ProductName");
}

หลังจากที่ทำการเปลี่ยนปลงทั้งหมดแล้ว ให้คุณทำการ  build  โซลูชันแล้วลองทดสอบดู ภาพที่ 3 แสดงหน้าจอของ SortableDataGrid.aspx  เมื่อเราเข้าไปดูเป็นครั้งแรก ส่วนภาพที่ 4 แสดงเว็บเพจหลักที่ผู้ใช้คลิกที่ไฮเปอร์ลิงก์หัวเรื่องคอลัมน์  Unit  Price  ถ้าหากดูจากภาพที่สอง เราจะพบว่า DataGrid ไม่สามารถจัดเรียงข้อมูลโดยใช้คอลัมน์  Units In Stock ได้ เนื่องจากเราไม่ได้กำหนด SortExpression property ให้แก่ BoundColumn นี้

 

ภาพที่ 3 DataGrid ที่จัดเรียงข้อมูลโดยใช้คอลัมน์ Product Name

 

ภาพที่ 4 DataGrid ที่จัดเรียงข้อมูลโดยใช้คอลัมน์ Unit Price

 

การสร้าง DataGrid ที่จัดเพจได้

สิ่งที่เหมือนกับ  DataGrid ที่จัดเรียงข้อมูลได้ก็คือการสร้าง DataGrid ที่จัดเพจได้เหมาะที่จะใช้สร้าง DataGrid ที่ต้องการให้แสดงผลข้อมูลอย่างไรก็ได้ การที่เราทำแบบนี้ได้ตั้งแต่ช่วงแรกของหัวข้อก่อนหน้านี้แล้ว ผมจึงจะไม่พูดถึงเรื่องนี้ซ้ำยกเว้นการบอกให้คุณสร้างเว็บเพจ  ASP.NET ที่ชื่อ PageableDataGrid.aspx ขึ้นมา แล้วทำการก็อปปี้ DataGrid  declaration ในส่วน HTML และโค้ดจาก Page_Load และ BindData() methods ในคลาสที่อยู่เบื้องหลังโค้ดจากเว็บเพจ  SortableDataGrid.aspx ก่อนหน้านี้ไปยังตำแหน่งที่เราพูดถึงการเพิ่มคุณสมบัติการจัดเรียงข้อมูลลงไปนั้นเอง

แม้ว่าคุณสามารถก็อปปี้  Page  Load  event  handler  มาใช้ได้ก็ตาม แต่คุณจำเป็นต้องแก้ไข BindData() method  เล็กน้อย นั่นก็คือแทนที่จะเชื่อมโยง SqlDataReader ไปยัง DataGrid คุณจำเป็นต้องใช้ DataTable หรือ DataSet แทน โดยเราจะพูดถึงเหตุผลของเรื่องนี้เล็กน้อย

ก่อนที่เราจะเพิ่มการจัดเพจให้แก่  DataGrid  สิ่งสำคัญที่คุณควรทราบก่อนก็คือ  DataGrid มีการจัดแบบฟอร์มของเพจได้สองแบบก็คือ

  • การจัดเพจแบบปกติ
  • การจัดเพจแบบพิเศษ

การจัดเพจแต่ละแบบมีข้อดีข้อเสียที่แตกต่างกันออกไป นั่นก็คือการจัดเพจปกตินำไปใช้ได้ง่ายกว่าการจัดเพจแบบพิเศษ แต่การจัดเพจพิเศษจะทำให้ประสิทธิภาพดีขึ้นกว่าเดิมอย่างมาก เหตุผลก็คือการจัดเพจแบบปกติจะเชื่อมโยงกับข้อมูลทั้งหมดของ  DataSource  ที่คุณต้องการจัดเพจผ่านทาง DataGrid รวมทั้งเกิดขั้นตอนที่ผู้ใช้ย้ายจากเพจหนึ่งไปยังอีกเพจหนึ่ง ถ้าหากพูดถึงการจัดเพจปกติแล้ว  DataGrid  จะรับผิดชอบกับการแยกแยะว่ามีเรกคอร์ดมากน้อยเพียงใด และเรกคอร์ดอะไรที่  DataSource  ต้องการใช้ในการแสดงผล แต่ในทางตรงข้ามการจัดเพจพิเศษจำเป็นต้องระบุอย่างชัดเจนให้มีเฉพาะเรกคอร์ดที่ต้องการแสดงผลในเพจปัจจุบันต้องเป็นข้อมูลใน   DataSource   ที่เชื่อมโยงกับ DataGrid เท่านั้น

การจัดเพจพิเศษนำมาใช้งานได้ง่ายกว่าเนื่องจากคุณไม่จำเป็นต้องทำอะไรเพิ่มเติมเมื่อผู้ใช้เปลี่ยนจากเพจหนึ่งไปสู่อีกเพจหนึ่ง นั่นก็คือคุณเพียงแต่เชื่อมโยงผลการคิวรี  SQL  ไปยัง Datagrid แล้วปล่อยให้ Datagrid จัดการกับการแสดงผลเรกคอร์ดเอง ถ้าหากเป็นการจัดเพจพิเศษคุณต้องใช้คำสั่ง SQL ที่ซับซ้อนหรือใช้ stored procedure ที่ยุ่งยากไม่แพ้กันเพื่อเลือกกลุ่มของเรกคอร์ดที่แน่นอนที่คุณต้องการนำมาแสดงผลในแต่ละเพจ การจัดเพจแบบพิเศษให้ประสิทธิภาพที่ดีกว่าการจัดเพจปกติเนื่องจากในแต่ละเพจมีเฉพาะเรกคอร์ดที่ต้องการแสดงผลเท่านั้น ถ้าหากเป็นการจัดเพจปกติ ทุกครั้งที่ผู้ใช้เรียกดูเพจข้อมูลที่ต่างกัน เรกคอร์ดทั้งหมดจะถูกเรียกขึ้นมา นอกจากนั้นการจัดเพจปกติบังคับให้คุณต้องเชื่อมโยงออปเจ็กต์ของ  DataTable หรือ DataSet ไปยัง DataGrid ด้วย นั่นก็คือคุณไม่สามารถใช้   DataReader  ได้ สาเหตุเนื่องจาก  DataGrid  จำเป็นต้องสามารถแยกแยะว่ามีเรกคอร์ดอยู่ใน DataSource มากน้อยเพียงใด จึงค่อยมากำหนดจำนวนหน้าสำหรับการแสดงผลข้อมูลได้

ส่วนในบทความนี้ เราจะเน้นไปที่การจัดเพจแบบปกติ เนื่องจากมันนำไปใช้งานได้ง่ายกว่า นอกจากนั้นการเน้นไปที่การจัดเพจแบบปกติก็ไม่ได้ทำให้เราเสียหายแต่อย่างใด เนื่องจากเทคนิคที่เราจะทำการทบทวนในหัวข้อต่อไปเป็นการสร้าง DataGrid ที่จัดเพจได้และจัดเรียงข้อมูลได้จะใช้ได้กับการจัดเพจแบบปกติและแบบพิเศษอยู่แล้ว

ขั้นตอนแรกของการจัดเพจ    DataGrid   ก็คือการใช้โมเดลการจัดเพจปกติ เพื่อกำหนดให้   AllowPaging property  ของ  DataGrid ให้เป็น True โดยปกติแล้ว property ตัวนี้ ถ้าหากกำหนดให้เป็น True จะแสดงอินเทอร์เฟซที่ค้นหาสิ่งที่ต้องการได้บริเวณด้านล่างของ   DataGrid  อินเทอร์เฟซค้นหาดังกล่าวจะช่วยให้ผู้ใช้เลือกเพจที่จะนำมาแสดงใน  DataGrid  ได้ ซึ่งโดยปกติแล้วอินเทอร์เฟซค้นหาจะใช้  LinkButtons เพื่อสร้างชุดไฮเปอร์ลิงก์ก่อนและหลังขึ้นมา เมื่อผู้ใช้คลิกไปที่ไฮเปอร์ลิงก์ตัวใดตัวหนึ่ง เว็บเพจของ  ASP.NET  จะโพสต์กลับไป พร้อมกับการเริ่มทำงานของ  PageIndexChanged  event เราไม่จำเป็นต้องสร้างตัวจัดการเหตุการณ์สำหรับเหตุการณ์นี้ ที่อัพเดต CurrentPageIndex property ของ DataGrid แล้วเชื่อมโยงข้อมูลซ้ำกลับไปหา DataGrid

CurrentPageIndex  property  ของ  DataGrid เป็นตัวกำหนดว่าจะแสดงผลเพจข้อมูลเพจไหน ซึ่งนอกเหนือจาก property ตัวนี้แล้ว ยังมี properties อื่นๆอีกสองสามตัวที่เกี่ยวข้องกับการจัดเพจ

  • PageSize: property ตัวนี้จะกำหนดว่า ในแต่ละเพจมีการแสดงผลที่เรกคอร์ด ค่าปกติก็คือ 10
  • PageCount: property ที่ใช้เป็นตัวกำหนดจำนวนเพจทั้งหมด

ดังนั้นตัวจัดการเหตุการณ์  PageIndexChanged  จึงต้องการกำหนดให้ CurrentPageIndex property ไปเป็นเพจที่ผู้ใช้เลือกเอาไว้เท่านั้น แล้วทำการเชื่อมโยงกับ DataGrid ซ้ำ (โดยการเรียก BindData()) นั่นก็คือเราสามารถแยกแยะได้ว่าเพจที่ต้องการเรียกดูคืออะไรโดยการอ้างอิง   NewPageIndex  property  ของออปเจ็กต์ DataGridPageChnagedeventArgs ซึ่งส่งผ่านไปให้ตัวจัดการเหตุการณ์นี้ได้

เมื่อมีการเรียก   DataBind()   method  ของ  DataGrid  ใน  BindData()  แล้ว  DataGrid  จะใช้ CurrentPageIndex  property เพื่อแยกแยะว่าจะแสดงเพจใด จากนั้นก็ใช้จำนวนของเรกคอร์ดสำหรับการแสดงในแต่ละหน้ามาคำนวณดูว่าเรกคอร์ใดควรจะแสดงผลเป็นเรกคอร์ดแรกในเพจจากนั้น  DataGrid  จะค้นหาเรกคอร์ดนี้ ตามด้วยการแสดงผลเรกคอร์ดนี้และเรกคอร์ดอื่นๆตามขอบเขตของ PageSize

ในตอนนี้เราต้องเพิ่มตัวจัดการเหตุการณ์ที่ชื่อ  dgProducts  Page  ลงไปยัง PageIndexChanged event ของ DataGrid โดยใช้เทคนิคที่พูดถึงในหัวข้อก่อนหน้านี้ ซอร์ซโค้ดของตัวจัดการเหตุการณ์นี้แสดงเอาไว้ด้านล่าง

private void dgProducts_Page(object source,
             System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
   dgProducts.CurrentPageIndex = e.NewPageIndex;
   BindData();
}

สิ่งที่เหมือนกับเว็บเพจ  SortableDataGrid.aspx  ก็คือเราต้องการแค่ตัวจัดการเหตุการณ์  Page Load เชื่อมโยงข้อมูลไปยัง  DataGrid ตอนที่มีการโหลดเพจครั้งแรก ไม่ใช่ตอนโพสต์กลับในครั้งต่อๆไป โค้ดด้านล่างนี้แสดงตัวจัดการเหตุการณ์   Page   Load  รุ่นอัพเดต และ  BindData()  method  สิ่งที่เราอยากบอกเอาไว้ก่อนก็คือ BindData()   method   ชื่อมโยง   DataTable   เข้าหา   DataGrid  ซึ่งตรงข้ามกับการเชื่อมโยงไปหา SqlDataReader   เราเคยบอกเอาไว้แล้วว่าการจัดเพจปกติไม่สามารถใช้   DataReader  ได้ แต่เราต้องใช้ DataTable  หรือ  DataSet  แทน โดยการใช้ DataReader จะส่งผลทำให้เกิดข้อยกเว้นขึ้นมา สาเหตุที่เราไม่สามารถใช้  DataReader ได้ก็เนื่องจาก DataGrid จำเป็นต้องสามารถแยกแยะว่ามีกี่เรกคอร์ดใน DataSource เพื่อที่จะคำนวณจำนวนเพจออกมาได้ การที่  DataReader รองรับการเรียกใช้แบบ forward เท่านั้น DataGrid จึงไม่สามารถแยกแยะว่ามีกี่เรกคอร์ดได้ ด้วยเหตุนี้เรื่องนี้จึงเป็นสิ่งที่จำเป็นสำหรับการจัดเพจปกติด้วยเช่นกันที่จะต้องแยกแยะให้ได้ว่ามีกี่เรกคอร์ด (เหมือนกับ DataTable หรือ DataSet)

private void Page_Load(object sender, System.EventArgs e)
{
   if (!Page.IsPostBack)
      BindData();
}
private void BindData()
{
   // Connect to the Database
   SqlConnection myConnection = new SqlConnection(connection string);
   // Retrieve the SQL query results and bind it to the DataGrid
   string SQL_QUERY = "SELECT ProductName, UnitPrice, UnitsInStock " +
                      "FROM Products";
   SqlCommand myCommand = new SqlCommand(SQL_QUERY, myConnection);
   // Use a DataTable – required for default paging
   SqlDataAdapter myAdapter = new SqlDataAdapter(myCommand);
   DataTable myTable = new DataTable();
   myAdapter.Fill(myTable);
   dgProducts.DataSource = myTable;
   dgProducts.DataBind();
   myConnection.Close();
}

 

ภาพที่  5  แสดงเพจ PageableDataGrid.aspx เมื่อเข้าไปดูเป็นครั้งแรกที่แสดงเรกคอร์ด 10 เรกคอร์ดแรกในตาราง  Products 

 

ภาพที่  6  แสดงเพจที่สองของข้อมูลที่แสดงผลขึ้นมาเมื่อผู้ใช้คลิกที่ไฮเปอร์ลิงก์ > ในอินเทอร์เฟซค้นหาจากภาพที่ 5

ตัวอย่างนี้ไม่ได้แสดงวิธีการปรับแต่งอินเทอร์เฟซค้นหาของ  DataGrid  ที่จัดเพจได้ อย่างไรก็ตามเราสามารถปรับแต่งให้แสดงข้อความอื่นๆสำหรับไฮเปอร์ลิงก์ก่อนและหลังได้ รวมทั้งการแสดงรายการของหมายเลขเพจอีกด้วย แทนที่จะเป็นลิงก์ก่อนและหลังเท่านั้น ที่จริงแล้ว ถ้าหากมีการเขียนโค้ดอีกเล็กน้อย คุณสามารถสร้างอินเทอร์เฟซการจัดเพจแบบพิเศษที่มีทั้งลิงก์ก่อนและหลังรวมทั้งรายการหมายเลขหน้าได้ ถ้าหากต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเรื่องเหล่านี้ให้เข้าไปดูได้ตามบทความดังต่อไปนี้

  • การกำหนดพฤติกรรมการจัดเพจในเว็บเซิร์ฟเวอร์คอนโทรล DataGrid
  • การจัดเพจตัวอักษร/ตัวเลขผสมตัวอักษรใน ASP.NET
  • การจัดเพจใน DataGrid

การสร้าง DataGrid ที่จัดเพจได้และจัดเรียงข้อมูลได้

เมื่อถึงจุดนี้เราได้ศึกษาวิธีการสร้าง  DataGrid  แบบจัดเรียงข้อมูลได้และ DataGrid ที่จัดเพจได้ผ่านไปแล้ว ในตอนนี้ได้เวลาที่จะรวมเอาคุณสมบัติทั้งสองชนิดให้กลายเป็น  DataGrid ที่ทั้งจัดเพจได้และจัดเรียงข้อมูลได้ ความยุ่งยากของการรวมเอาคุณสมบัติทั้งสองชนิดไปเป็น  DataGrid  เพียงอันเดียวมาจากข้อเท็จจริงที่ว่าแนวทางแต่ละแบบใช้รูปแบบของ  BindData()  method  ที่แตกต่างกัน  DataGrid  ที่จัดเรียงข้อมูลได้ส่งสตริงพารามิเตอร์ไปยัง BindData() ซึ่งเป็นตัวกำหนดว่าจะจัดเรียงข้อมูลของคอลัมน์ใดใน DataGrid แต่ในทางตรงข้าม Datagrid ที่จัดเพจได้จะไม่ทำงานในลักษณะดังกล่าว

ความพยายามในการแก้ไขความแตกต่างเบื้องต้นก็คือการใช้  BindData() เวอร์ชันที่ยอมรับสตริงพารามิเตอร์และมีตัวจัดการเหตุการณ์  PageIndexChanged ของ DataGrid ที่เอาไว้คอยส่งค่าสตริงที่กำหนดเอาไว้แล้วมาให้เสมอ ตัวอย่างเช่นการที่ตัวจัดการเหตุการณ์  PageIndexChanged  ต้องเรียก BindData() เพื่อส่งสตริง ดังนั้นเราจึงสามารถใช้โค้ดดังต่อไปนี้สำหรับตัวจัดการเหตุการณ์นี้ได้

private void dgProducts_Page(object source,
             System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
   dgProducts.CurrentPageIndex = e.NewPageIndex;
BindData("ProductName");
}

อย่างไรก็ตามแนวทางนี้ไม่สามารถใช้งานได้ เมื่อพิจารณาการเกิดลำดับเหตุการณ์ต่อเนื่องดังนี้

  1. เมื่อผู้ใช้เข้าไปในเพจ ผู้ใช้คนนี้เห็นเพจแรก ที่จัดเรียงข้อมูลโดยอิงกับฟิลด์ ProductName อยู่
  2. ผู้ใช้คลิกไปที่ไฮเปอร์ลิงก์หัวเรื่องคอลัมน์ Unit Price เพื่อให้แสดงเพจแรกที่ข้อมูลจัดเรียงตาม Unit Price
  3. ผู้ใช้คลิกไปที่ไฮเปอร์ลิงก์ค้นหา > เพื่อเรียกข้อมูลเพจต่อไป

ขั้นตอนนี้ส่งผลทำให้ตัวจัดการเหตุการณ์   PageIndexChanged   ประมวลผลโพสต์กลับซึ่งเรียกข้อมูลที่จัดเรียงตาม ProductName  ขึ้นมา ดังนั้นในตอนนี้ผู้ใช้จะเห็นเพจที่สองที่จัดเรียงข้อมูลตาม  ProductName แทนที่จะเป็นเพจที่สองของข้อมูลที่จัดเรียงตาม UnitPrice

เราต้องการวิธีบางอย่างเพื่อให้ระบบจำได้ว่าฟิลด์อะไรที่เราต้องการจัดเรียงเมื่อเรียก   BindData()  จากตัวจัดการเหตุการณ์   PageIndexChanged   โดยที่มีวิธีการหลายแบบที่ใช้เก็บรักษาสภาพไปกลับไปยังเซิร์ฟเวอร์โดยใช้ ASP.NET อย่างไรก็ตามการที่เราต้องการเซฟข้อมูลนี้เอาไว้สำหรับเพจปัจจุบันเท่านั้น ไม่ใช่สำหรับผู้ใช้ปัจจุบัน ดังนั้นแนวทางที่ดีที่สุดก็คือการจัดเก็บคอลัมน์ที่ผู้ใช้ต้องการจัดเรียงเอาไว้ใน ViewState ของเว็บเพจ

บทความนี้ไม่ต้องการพูดถึง  ViewState  อย่างละเอียด อย่างไรก็ตามเราจะพูดถึงการนำเอา ViewState มาใช้ประโยชน์ในการสร้าง   DataGrid  ที่จัดเรียงข้อมูลและจัดเพจได้เพียงเล็กน้อย ก่อนอื่นสิ่งที่คุณควรทราบก็คือคอนโทรลทุกชนิดในเว็บเพจ  ASP.NET  มี  ViewState และเมื่อคอนโทรลเหล่านี้ถูกใส่เอาไว้ใน WebForm (<form runat  =  "server">)  ViewState จะถูกเซฟเอาไว้ในฟิลด์ฟอร์ม HTML ที่ซ่อนเอาไว้ เราสามารถเขียนโปรแกรมให้ใส่ออปเจ็กต์ลงไปใน  ViewState  ได้ โดยที่ออปเจ็กต์ที่ใส่ลงไปนี้สามารถเซฟเอาไว้ร่วมกับข้อมูลอื่นๆของ ViewState    ได้ ซึ่งนั่นหมายความว่าระบบจะจำค่าได้ตอนที่โพสต์กลับไป ถ้าหากต้องการทราบข้อมูลอื่นๆของ ViewState กรุณาอ่านบทความ "การใช้ประโยชน์จาก ViewState ใน ASP.NET"

ซินแทกซ์ของการใส่และเรียกองค์ประกอบลงไปใน ViewState ให้ปฏิบัติดังนี้

// C#
object o = ViewState[key];   // retrieval
ViewState[key] = o;      // assignment
' VB.NET
Dim o as Object = ViewState(key)   ' retrieval
ViewState(key) = o         'assignment

คีย์ก็คือสตริงคีย์ที่เตรียมชื่อเอาไว้สำหรับออปเจ็กต์ที่จัดเก็บอยู่ใน ViewState

ในตอนนี้เมื่อใดก็ตามที่ผู้ใช้คลิกที่ไฮเปอร์ลิงก์หัวเรื่องคอลัมน์ของ DataGrid เราจะเห็นองค์ประกอบ 3 อย่างก็คือ

  1. มีการรีเซ็ต CurrentPageIndex ของ DataGrid ไปเป็น 0
  2. จัดเก็บ  SortExpression  property  ของพารามิเตอร์  DataGridSortCommnad_EventArgs ไปไว้ใน ViewState ร่วมกับค่าคีย์ SortExprValue
  3. เรียก BindData() โดยส่งค่า SortExpression ออกไป

ขั้นตอนที่   1  จะทำให้  DataGrid  ย้อนกลับไปยังเพจแรกของข้อมูลเมื่อใดก็ตามที่ผู้ใช้คลิกที่ไฮเปอร์ลิงก์หัวเรื่องของคอลัมน์  DataGrid วิธีการนี้จัดว่าเหมาะสมแล้ว ถ้าหากสมมติว่าในตอนนี้ผู้ใช้เรียกดูข้อมูลหน้า 3 ที่จัดเรียงโดย Product  Name  อยู่ แล้วผู้ใช้ต้องการเห็นผลลัพธ์ที่จัดเรียงโดย Unit Price พวกเขาควรจะถูกส่งไปยังหน้าแรกของผลลัพธ์ แล้วเห็นรายการสินค้าที่มีราคาถูกที่สุดก่อน ถ้าหากเราไม่ได้รีเซ็ต CurrentPageIndex ไปเป็น 0 ในตัวจัดการเหตุการณ์  SortCommand แล้ว ผู้ใช้ที่ต้องการจัดเรียงสินค้าโดยอิงกับ Unit Price จะเห็นข้อมูลหน้า 3 ซึ่งจัดเรียงตามฟิลด์ UnitPrice ผู้ใช้คงจะรู้สึกสับสนอย่างมาก

ตัวจัดการเหตุการณ์ SortCommand อันใหม่ควรมีหน้าตาแบบนี้

private void dgProducts_Sort(object source,
          System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
{
dgProducts.CurrentPageIndex = 0;
ViewState["SortExprValue"] = e.SortExpression;
BindData(e.SortExpression);
}

ตัวแปร  ViewState นี้จะช่วยให้การเขียนตัวจัดการเหตุการณ์ PagaeIndexChanged ง่ายขึ้นอย่างมาก เราเพียงแต่กำหนดให้  PageIndexChanged  property  ของ  DataGrid  เท่ากับ NewPageIndex property ของ DataGridPageChangedEventArgs  เหมือนที่เราเคยทำมาแล้ว จากนั้นเรียก BindData() เพื่อส่งค่าออปเจ็กต์ของ ViewState ที่ชื่อ SortExprValue ออกไปเท่านั้น

private void dgProducts_Page(object source,
           System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
dgProducts.CurrentPageIndex = e.NewPageIndex;
BindData(ViewState["SortExprValue"].ToString());
}

การทำงานขั้นสุดท้ายก็คือการอัพเดตตัวจัดการเหตุการณ์      Page-Load     เพื่อที่ออปเจ็กต์     ViewState SortExprValue จะถูกกำหนดให้เป็นคอลัมน์ที่เราต้องการให้ DataGrid ทำการจัดเรียงข้อมูลตามปกติ การเปลี่ยนแปลงเล็กน้อยต่อไปนี้ช่วยให้เราได้ตัวจัดการเหตุการณ์ Page_Load อันใหม่ขึ้นมา

private void Page_Load(object sender, System.EventArgs e)
{
   if (!Page.IsPostBack)
   {
ViewState["SortExprValue"] = "ProductName";
BindData(ViewState["SortExprValue"].ToString());
   }
}

และนั่นก็คือวิธีการทั้งหมด เนื้อหาส่วนที่เหลือของบทความนี้แสดงภาพตัวอย่างของ DataGrid ที่จัดเรียงข้อมูลได้และจัดเพจได้ นอกจากนั้นคุณยังสามารถดาวน์โหลดซอร์ซโค้ดทั้งหมดของบทความนี้ได้ในชวงท้ายอีกด้วย

ภาพที่   7   แสดงภาพของ  DataGrid  ที่จัดเรียงข้อมูลได้และจัดเพจได้เมื่อตอนที่เข้าไปครั้งแรกทางบราวเซอร์ ส่วนภาพที่  8 แสดงภาพเพจที่สองของข้อมูล จัดเรียงตาม Product Name ส่วนภาพที่ 9 แสดงผลลัพธ์หลังจากที่ผู้ใช้ต้องการแสดงผลลัพธ์ที่จัดเรียงตาม  Unit Price ในขณะที่ภาพที่ 10 ภาพแสดงเพจที่สองของข้อมูลที่เรียงลำดับตาม Unit Price

 

ภาพที่  7  DataGrid  ที่จัดเรียงข้อมูลได้และจัดเพจได้เมื่อเข้ามาดูเป็นครั้งแรก  (เรียงข้อมูลตามชื่อ  Product Name)

 

ภาพที่ 8 เพจที่สองของข้อมูลเมื่อจัดเรียงตาม Product Name

 

ภาพที่ 9 หน้าแรกของข้อมูลเมื่อจัดเรียงตาม Unit Price

ภาพที่ 10 หน้าที่สองของข้อมูลเมื่อจัดเรียงตาม Unit Price

 

สรุป

การสร้าง   Datagrid   ที่จัดเพจได้หรือจัดเรียงข้อมูลได้จัดเป็นเรื่องง่ายมาก อย่างไรก็ตามการรวมคุณสมบัติทั้งสองชนิดไปเป็น    DataGrid    เพียงอันเดียวจัดเป็นเรื่องที่ยุ่งยากกว่าเล็กน้อย เนื่องจากเราต้องจำให้ได้ว่า DataGrid ใช้คอลัมน์ใดในการจัดเรียงข้อมูลในเพจ สิ่งที่เราเห็นในบทความนี้ก็คือเราสามารถใช้ ViewState เพื่อช่วยเราจำฟิลด์  DataSource  ได้ เพื่อที่ DataGrid จะได้นำไปเรียงลำดับข้อมูล แม้ว่าบทความนี้เน้นไปที่การจัดเพจปกติก็ตาม แต่เทคนิคนี้สามารถนำไปประยุกต์ใช้กับการจัดเพจพิเศษได้ด้วยเช่นกัน

ถ้าหากคุณมีคำถามเกี่ยวกับบทความนี้ไม่ว่าจะเป็นเทคนิคที่พูดถึงไปแล้ว หรือตัวอย่างโค้ดที่นำไปทดสอบก็ตาม คุณสามารถส่งอีเมล์มาสอบถามได้ที่ mitchell@4guysfromrolla.com

ขอให้มีความสุขกับการเขียนโปรแกรม

Filed under: ,
No Comments

Leave a Comment

(required)  
(optional)
(required)  
Add