富數據控件 GridView(行選擇、排序、分頁)


GridView 行選擇

       GridView 內建支持選擇。只需加入 ShowSelectButton 屬性為 true 的 CommandField 列即可。CommandField 可以呈現為超鏈接、按鈕或固定的圖片。使用ButtonType 屬性選擇類型后,就可以通過 SelectText 屬性(默認為 Select )指定文字或者通過 SelectImageUrl 屬性指定圖片的鏈接。

<asp:CommandField ShowSelectButton="true" ButtonType="Image" SelectImageUrl="~/images/Print.ico"/>
<asp:CommandField ShowSelectButton="true" ButtonType="Button" />

image

       單擊選擇按鈕時,頁面回傳並展開一系列步驟:

  1. 首先,GridView.SelectedIndexChanging 事件發生,可以響應它取消操作。
  2. 接着,調整 GridView.SelectedIndex 屬性指向當前選中的行。
  3. 最后,GridView.SelectedIndexChanged 事件發生,可以在該事件中手動更新其他控件以反映用戶的新選擇。
  4. 頁面呈現后,SelectedRowStyle 作用於改行。

使用選擇來創建 主-從表單

       你可以在頁面中加入兩個 GridView 控件,並用從第一個 GridView 中獲得的信息在第二個中執行查詢。對於 GridView 而言,需要綁定的屬性是 SelectedIndex,不過這里有一個問題,SelectedIndex 返回的是一個基於零的數字,它反映在網格中的位置。這並非獲得相關記錄的查詢所需的信息。相反,你需要當前行的關鍵字段。幸好,GridView 通過SelectedDataKey 屬性可以輕易獲得這些信息。要使用該特性,你必須用逗號分隔的一個或多個關鍵字段的列表來設置 GridView.DataKeyNames 屬性。你指定的每個名字必須和綁定對象的屬性或綁定記錄的字段相匹配!(一般情況下只有一個關鍵字段。)

<asp:GridView ID="GridView1" runat="server" DataSourceID="sourceEmployees" DataKeyNames="EmployeeID" ....../>

       現在可以把第二個數據源綁定到這個字段上進行查詢。這個示例在聯合查詢中使用 EmployeeID 查找 Territories 表中匹配的記錄,得到指定雇員管理的所有區域:

<asp:SqlDataSource ID="sourceRegions" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>"
    ProviderName="System.Data.SqlClient" SelectCommand="select Employees.EmployeeID,Territories.TerritoryID,Territories.TerritoryDescription from Employees 
 inner join EmployeeTerritories on Employees.EmployeeID =EmployeeTerritories.EmployeeID inner join Territories on 
 EmployeeTerritories.TerritoryID = Territories.TerritoryID where (Employees.EmployeeID = @EmployeeID)">
    <SelectParameters>
        <asp:ControlParameter ControlID="GridView1" Name="EmployeeID" PropertyName="SelectedDataKey.Values[&quot;EmployeeID&quot;]" />
    </SelectParameters>
</asp:SqlDataSource>
<asp:GridView ID="GridView2" runat="server" DataSourceID="sourceRegions">
</asp:GridView>

       本例中的查詢參數從上一個 GridView 中的 SelectedDataKey.Values 集合中獲取,也可以使用索引(本例中是0,因為DataKeyNames 列表中只有一個字段)。使用名字查找時唯一要注意的小技巧是必須使用相應的 HTML 字符實體(&quot;)代替雙引號。

image

 

SelectedIndexChanged 事件

       很多情況下,還是需要響應 SelectedIndexChanged 事件。可能需要重定向用戶到某個頁面(在查詢字符串中使用選定的值)或者需要調整同一頁面的其他控件。

protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
    int index = GridView1.SelectedIndex;
 
    // 可以獲取選中行的數據鍵值
    int ID = (int)GridView1.SelectedDataKey.Values["EmployeeID"];
 
    // 可以從單元格集合中獲取值,前提是要知道索引
    string firstName = GridView1.SelectedRow.Cells[3].Text;
    string lastName = GridView1.SelectedRow.Cells[4].Text;
    Label1.Text = firstName + " " + lastName + " ID:" + ID;
}

 

將數據字段用作選擇按鈕

       很多時候並不需要創建一個新列去支持行選擇,而是可以把一個現有的數據列變為鏈接,該技術廣泛應用於幫助用戶通過唯一標識符選定表中的行。單擊后頁面會觸發 GridView.RowCommand 事件,可以處理這個事件通過編程來設置 SelectedIndex 屬性,不過更為簡單的辦法是指定 CommandName 屬性的內容為 "Select" ,就可以自動配置響應 GridView.SelectedIndexChanged 事件了。

<asp:ButtonField ButtonType="Button" DataTextField="EmployeeID" CommandName="Select"/>

 

 

對 GridView 排序

       為了啟用排序,必須設置 GridView.AllowSorting 屬性為 true,為每個可排序的列定義排序表達式。排序表達式一般使用 SQL 查詢中 ORDER BY 子句的形式。

<asp:BoundField DataField="FirstName" HeaderText="First Name" SortExpression="FirstName" />

       一旦設置了排序表達式並已經把 AllowSorting 設為 true 后,GridView 會把標題呈現為可單擊的鏈接。

       實現真實的排序邏輯是數據源控件的責任,排序如何實現取決於你使用的數據源,並非所有的數據源都支持排序,但 SqlDataSource 和 ObjectDataSource 執持。

 

使用 SqlDataSource 排序

       使用 SqlDataSource 排序時,排序由 DataView 類內置的排序功能實現。用戶單擊鏈接時,DataView.Sort 屬性被設置為列的排序表達式。

image

 

使用 ObjectDataSource 排序

       ObjectDataSource 提供了兩種排序方法:

  • 如果選擇方法返回的是 DataSet 或者 DataTable,ObjectDataSource 可以使用和 SqlDataSource 一樣的自動排序。
  • 如果選擇方法返回的是自定義集合,那么需要提供一個接收排序表達式並執行排序的方法。這樣的方式為你構建解決方案提供了足夠的靈活性,但是它未必足夠理想。例如,創建一個帶有 Sort()方法的自定義的 EmployeeDetails 集合更有意義,而不是創建一個可以執行排序的 GetEmployee()方法。遺憾的是,ObjectDataSource 不支持這種模式。

       你需要創建一個接收字符串作為參數的方法來使用排序參數。你還必須讓 ObjectDataSource .SortParameterName 屬性使用和這個參數一模一樣的名字

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" TypeName="Model.EmployeeDB"
    SelectMethod="GetEmployees" SortParameterName="sortExpression"></asp:ObjectDataSource>

       設置了 SortParameterName 后,ObjectDataSource 總是調用接收排序表達式的那個版本的方法(網格第一次打開時,ObjectDataSource 傳遞一個空字符串作為排序表達式)。

       實現可排序的 GetEmployees()方法最簡單的做法是,填充一個非連接的 DataSet,使用 DataView 的排序功能:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" TypeName="Model.EmployeeDB"
    SelectMethod="GetEmployees" SortParameterName="sortExpression"></asp:ObjectDataSource>
<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1" AllowSorting="true">
</asp:GridView>
public EmployeeDetails[] GetEmployees(string sortExpression)
{
    SqlConnection conn = new SqlConnection(conStr);
    SqlCommand cmd = new SqlCommand("GetAllEmployees", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlDataAdapter sda = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    try
    {
        conn.Open();
        sda.Fill(ds, "Employees");
    }
    catch (Exception err)
    {
        throw new ApplicationException("Data Error!");
    }
    finally
    {
        conn.Close();
    }
 
    DataView view = ds.Tables[0].DefaultView;
 
    // 這里將傳入的 表達式 賦值上去
    view.Sort = sortExpression;
 
    ArrayList employees = new ArrayList();
    foreach (DataRowView row in view)
    {
        EmployeeDetails emp = new EmployeeDetails((int)row["EmployeeID"],
            row["FirstName"].ToString(), row["LastName"].ToString(),
            row["TitleOfCourtesy"].ToString());
        employees.Add(emp);
    }
    // 這里需要類型轉換
    return (EmployeeDetails[])employees.ToArray(typeof(EmployeeDetails));
}

image

 

排序和選擇

       如果同時使用了選擇和排序,很快就會發現另一個問題,選定一行然后用任意一列進行排序,你會發現選擇行仍然保留,但是它已經變為和原來選擇具有相同序號的其它項目了。

       過去為了解決這個問題,辦法是在每次單擊標題連接后通過代碼改變選擇項。但 ASP.NET 給 GridView 增加了一個 EnablePersistedSelection 屬性,把它設為 true,ASP.NET 就會確保選中項由數據鍵值來決定

 

高級排序

       增強 GridView 排序功能的第一個辦法就是處理 GridView.Sorting 事件,它在排序前發生。

protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
    // 前者獲取的是將要進行排序的表達式
    // 后者獲取的是排序前的 GridView 排序表達式(即前一次的)
    if (e.SortExpression == "FirstName" && GridView1.SortExpression=="LastName")
    {
        e.SortExpression = "LastName,FirstName";
    }
}

       再進一步,還可以實行任意列集合的級聯排序,你可以在視圖狀態里記住用戶過去所做的排序操作,然后用它們創建一個大的排序表達式。不過這樣做也許會導致用戶直覺的混淆,會有很多別的問題。這里只是說明了這種操作的可能性。

       還可以獲取下拉列表的選擇的值,用代碼來調用 GridView.Sort()方法實現排序:

protected void ddlSorts_SelectedIndexChanged(object sender, EventArgs e)
{
    GridView1.Sort(ddlSorts.SelectedValue, SortDirection.Descending);
}

 

 

GridView 分頁

       GridView 控件對分頁提供內建的支持。你可以和 SqlDataSource 或 ObjectDataSource 一起實現簡單的分頁。后者還可以使用跟高效、更具靈活性的方式來實現自定義分頁。

 

自動分頁

       設置一些屬性並處理一個事件后,就可以讓 GridView 控件代為管理分頁。GridView 提供了幾個專為支持分頁而設計的屬性:

AllowPaging 啟用或禁用分頁(默認為禁用)
PageSize 獲取或設置網格中每頁顯示的記錄數(默認值 10)
PageIndex 分頁啟用時,獲取或設置基於零的當前頁碼
PagerSettings 提供一個為分頁控件封裝了各種格式化選項的 PagerSettings 對象。這些選項決定分頁控件出現的位置、包含的圖片或文本。可以設置這些屬性來精化分頁控件的外觀。
PagerStyle 提供一個樣式對象,使用它可以配置分頁控件的字體、顏色以及文字對齊方式。
PageIndexChanging 事件 導航之前發生
PageIndexChanged 事件 導航之后發生

       下面示例聲明一個簡單的分頁:

<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1" 
    AllowPaging="true" PageSize="3">
</asp:GridView>

image

 

       自動分頁可以和任意實現了 ICollection 接口的數據源一起使用。也就是說,只要使用 DataSet 模式,SqlDataSource 就支持自動分頁。此外,如果自定義的數據訪問類返回實現了 ICollection 接口的對象,數組、強類型集合、非連接的 DataSet 都是有效的選項,ObjectDataSource 也支持分頁。

       自動分頁並沒有減少數據庫查詢的數據量。相反,每次用戶改變了當前頁碼時都需要獲取和綁定所有數據。使用數據源控件的自動緩存可以大幅度提升自動分頁的性能,但巨大的查詢結果並不適合在內存中緩存,因此,最好的解決方案是自定義分頁

 

分頁和選擇

       默認情況下,分頁和選擇不能一起很好的工作。如果同時啟用二者,就會發現在頁面跳轉時之前選擇的行號一直保持選中狀態。為了修復這個缺陷,可以設置 GridView 的 EnablePersistedSelection 屬性為 true。這樣,在頁面跳轉時選中項會自動被移除(SelectedIndex 被設為 -1),但如果返回到原始選中行的所在頁,選中狀態將重新恢復。

 

使用 ObjectDataSource 的自定義分頁

       自定義分頁需要你負責為 GridView 析取和綁定當前頁的記錄。GridView 不再自動選擇要顯示的行。不過,GridView 還將提供帶有自動生成的鏈接的分頁欄。這些鏈接允許用戶在頁間導航。

       盡管自定義分頁遠比自動分頁復雜,但它允許你最小化帶寬需求並避免在服務器端內存中保存大量的數據。但另一方面,所有的自定義分頁策略都需要在每次回發時訪問數據庫,它也意味着可能給數據庫帶來更多的工作。

       ObjectDataSource 是唯一支持自定義分頁的數據源。

       自行管理分頁的步驟:

  1. 設置 ObjectDataSource.EnablePaging 為 true
  2. 通過 3 個屬性的設置來實現分頁:
    1. StartRowIndexParameterName
    2. MaxmumRowsParameterName
    3. SelectCountMethod

1. 記錄計數

       為了讓 GridView 能夠正確創建頁碼的鏈接,它必須知道記錄的總數以及每頁顯示的記錄數。自定義分頁時,必須通過專門的方法顯式計算記錄總數。

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" EnablePaging="true" TypeName="Model.EmployeeDB"
    SelectCountMethod="CountEmployees" ......></asp:ObjectDataSource>

2. 使用獲得分頁記錄的存儲過程

       GetEmployees()方法不再獲取所有員工信息了,而只是獲取當前頁面顯示的記錄,因此新建一個存儲過程來完成這個有條件的查詢:

create proc GetEmployeePage
@Start int,@Count int
as
Create table #TempEmployees --創建臨時表
(
    ID                int    identity primary key,
    EmployeeID        int,
    LastName        nvarchar(20),
    FirstName        nvarchar(10),
    TitleOfCourtesy    nvarchar(25),
)
 
-- 填充記錄到零時表
insert into  #TempEmployees
(
    EmployeeID,LastName,FirstName,TitleOfCourtesy
) 
select 
    EmployeeID,LastName,FirstName,TitleOfCourtesy 
from 
    Employees order by EmployeeID asc
 
declare @FromID int
declare @ToID int
set @FromID = @Start
set @ToID = @Start + @Count - 1
 
select * from #TempEmployees where ID >= @FromID and ID <= @ToID

3. 分頁的選擇方法      

       最后是創建一個重載的 GetEmployees()方法來執行分頁。這個方法接受2個參數:分頁開始的行號(從0開始)和 每頁的大小(最大行數)。通過 ObjectDataSource 的 StartRowIndexParameterName 和 MaxmumRowsParameterName 屬性指定要使用的參數名字(不指定的話,默認為 startRowIndex 和 maximumRows)。

public EmployeeDetails[] GetEmployees(int startRowIndex, int maximumRows)
{
    SqlConnection conn = new SqlConnection(conStr);
    SqlCommand cmd = new SqlCommand("GetAllEmployees", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add(new SqlParameter("@Start", SqlDbType.Int, 4));
    cmd.Parameters["@Start"].Value = startRowIndex + 1;
    cmd.Parameters.Add(new SqlParameter("@Count", SqlDbType.Int, 4));
    cmd.Parameters["@Count"].Value = maximumRows;
 
    ArrayList employees = new ArrayList();
 
    try
    {
        conn.Open();
        SqlDataReader reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            EmployeeDetails emp = new EmployeeDetails((int)reader["EmployeeID"],
                reader["FirstName"].ToString(), reader["LastName"].ToString(),
                reader["TitleOfCourtesy"].ToString());
            employees.Add(emp);
        }
        reader.Close();
        return (EmployeeDetails[])employees.ToArray(typeof(EmployeeDetails));
    }
    catch (Exception err)
    {
        throw new ApplicationException("Data Error!");
    }
    finally
    {
        conn.Close();
    }
}
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" EnablePaging="true" TypeName="Model.EmployeeDB"
    SelectCountMethod="CountEmployees" SelectMethod="GetEmployees"></asp:ObjectDataSource>
<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1" AllowPaging="true" PageSize="3">
</asp:GridView>

image

 

 

定制分頁欄

       GridView 分頁控件非常靈活。你可以通過 PagerStyle(可設置背景色、前景色、字體、顏色、大小)和 PagerSettings 屬性徹底改變它的外觀。

<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1" AllowPaging="true" PageSize="3">
    <PagerSettings Mode="NextPrevious" PreviousPageText="< Back" NextPageText="Forward >" />
    <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
</asp:GridView>

image

       最重要的細節在於 PageSettings.Mode 屬性,它根據下表的幾個模式決定分頁鏈接呈現的風格:

Numeric 網格將呈現 PagerSettings.PageButtonCount 屬性定義的頁數的鏈接。
NextPrevious 網格只呈現兩個鏈接,用於跳轉到前一頁和后一頁。還可以設置顯示的文字。
NumericFirstLast 和Numeric相同,但同時顯示第一頁和最后一頁的鏈接。
NextPreviousFirstLast 和NextPrevious相同,同時顯示第一頁和最后一頁的鏈接。

       如果不喜歡默認的分頁欄,還可以使用模版功能創建一個 PagerTemplate 來實現自己的分頁欄。

 

排序和分頁回調

       用網格排序和分頁的一個缺點是,每次對網格重新排序或移動到另一頁時,瀏覽器需要觸發一個回送並呈現一個全新的 HTML 頁。這意味着頁面將閃動並回滾到開始,這使得整個用戶體驗出現了一點不和諧。

       GridView 中的一個特性可以改善這種情形,即 EnableSortingAndPagingCallbacks 屬性。如果將該屬性設為 true,那么 GridView 將使用另外一種技術來刷新頁面。當單擊列標題或頁面鏈接時,瀏覽器向服務器發送一個異步請求,而不是強制執行一個回送來獲得新信息。當瀏覽器收到該信息時,使用 HTML DOM 技術來修改當前頁面。這一技術創造了一種更加無縫的、無閃動的瀏覽體驗。最重要的是,如果瀏覽器不支持這一特性,GridView 會優雅的降到標准的回送模式。唯一的限制是,你不能在使用模版的網格上使用排序和分頁回調。

       EnableSortingAndPagingCallbacks 屬性使用的是 ASP.NET 的回調架構。實際上是用 JavaScript 和 XMLHttpRequest 對象來執行回調的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM