国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

創(chuàng)建用于ASP.NET的分頁程序控件

2024-07-10 13:05:33
字體:
供稿:網(wǎng)友


摘要:解決向任何 asp.net 控件添加分頁功能的問題。還為開發(fā)復(fù)合 asp.net 控件提供了很多有用的提示和技巧。

下載本文的源代碼(英文)。(請(qǐng)注意,在示例文件中,程序員的注釋使用的是英文,本文中將其譯為中文是為了便于讀者理解。)

從程序員的角度來看,microsoft® sql server™ 查詢的最大缺陷之一就是返回的行數(shù)通常比應(yīng)用程序的用戶界面實(shí)際可以容納的行數(shù)要多得多。這種尷尬情形經(jīng)常將開發(fā)人員陷于困境。開發(fā)人員是應(yīng)該創(chuàng)建一個(gè)非常長的頁面,讓用戶花時(shí)間去滾動(dòng)瀏覽,還是應(yīng)該通過設(shè)置一個(gè)手動(dòng)分頁機(jī)制來更好地解決這個(gè)問題?

哪種解決方案更好,在很大程度上取決于要檢索的數(shù)據(jù)的特性。由多個(gè)項(xiàng)目(如搜索結(jié)果)組成的較長列表,最好通過各頁大小相等、每頁相對(duì)較短的多個(gè)頁面顯示。由單個(gè)項(xiàng)目(如文章的文本)組成的較長列表,如果整個(gè)插入在一個(gè)頁面中,使用起來會(huì)更方便。最后得出的分析結(jié)果是,應(yīng)該根據(jù)應(yīng)用程序的總體用途來做決定。那么,microsoft® asp.net 是如何解決數(shù)據(jù)分頁問題的呢?

asp.net 提供了功能強(qiáng)大的數(shù)據(jù)綁定控件,以便將查詢結(jié)果格式化為 html 標(biāo)記。但是,這些數(shù)據(jù)綁定控件中只有一種控件(即 datagrid 控件)本來就支持分頁。其他控件(如 datalist、repeater 或 checkboxlist)則不支持分頁。這些控件及其他列表控件不支持分頁,不是因?yàn)樗鼈冊(cè)诮Y(jié)構(gòu)上不支持分頁,而是因?yàn)樗鼈兣c datagrid 不同,不包含任何處理分頁的特定代碼。但是,處理分頁的代碼是相當(dāng)樣板化的,可以添加到所有這些控件中。

scott mitchell 在最近的一篇題目為“creating a pageable, sortable datagrid”(英文)的文章中,介紹了 datagrid 分頁。該文還引用了 web 上的其他有用信息,為您提供了有關(guān)網(wǎng)格分頁基礎(chǔ)知識(shí)和其他信息。如果想查看如何使 datalist 控件可以進(jìn)行分頁的示例,可以查看此文章(英文)。該文演示了如何創(chuàng)建一個(gè)自定義的 datalist 控件,該控件具有當(dāng)前索引和頁面大小屬性,并可以啟動(dòng)頁面更改事件。

同樣的代碼也可以用于滿足其他列表控件(如 listbox 和 checkboxlist)的分頁需要。不過,向各個(gè)控件添加分頁功能實(shí)際上并不是一種非常好的做法,因?yàn)椋缟纤觯猪摯a是相當(dāng)樣板化的。因此,對(duì)于聰明的程序員來說,有什么方法比使用一種新的通用分頁程序控件來實(shí)現(xiàn)所有這些控件的分頁功能更好的呢?

本文中將建立一個(gè)分頁程序控件,它將使合作者列表控件能夠?qū)?sql server 的查詢結(jié)果進(jìn)行分頁。該控件名為 sqlpager,它支持兩種類型的合作者控件 - 列表控件和基礎(chǔ)數(shù)據(jù)列表控件。

sqlpager 控件的顯著特點(diǎn)

sqlpager 控件是一個(gè) asp.net 復(fù)合控件,包含一個(gè)單行表格。該行又包含兩個(gè)單元格 - 導(dǎo)航條和頁面描述符。該控件的用戶界面呈條形,理想情況下,其寬度與合作者控件的寬度相同。導(dǎo)航條部分提供了可單擊的元素,以便在頁面之間移動(dòng);頁面描述符部分為用戶提供了有關(guān)當(dāng)前顯示的頁面的一些反饋信息。



圖 1:visual studio .net 網(wǎng)頁設(shè)計(jì)器中顯示的 sqlpager 控件

與 datagrid 控件的嵌入式分頁程序一樣,sqlpager 控件具有兩種導(dǎo)航模式,即下一頁/上一頁和數(shù)字頁面。此外,其特殊屬性 pagerstyle 使您能夠選擇更方便的樣式。該控件與列表控件協(xié)同工作。您可以通過 controltopaginate 字符串屬性為分頁程序指定一個(gè)這樣的合作者控件。

sqlpager1.controltopaginate = "listbox1";

一般情況下,分頁程序首先獲取 sql server 的查詢結(jié)果,準(zhǔn)備一個(gè)適當(dāng)?shù)挠涗涰撁妫缓笸ㄟ^合作者控件的 datasource 屬性顯示該頁面。當(dāng)用戶單擊以查看新頁面時(shí),分頁程序?qū)z索請(qǐng)求的數(shù)據(jù)并再次通過合作者控件來顯示數(shù)據(jù)。分頁機(jī)制對(duì)于列表控件是完全透明的。列表控件的數(shù)據(jù)源是通過編程方式進(jìn)行更新的,任何時(shí)候都只包含適合當(dāng)前頁面的記錄。

控件的分頁引擎具有多個(gè) public 屬性,如 currentpageindex、itemsperpage 和 pagecount,通過這些屬性來獲取并設(shè)置當(dāng)前頁面的索引、每個(gè)頁面的大小以及要顯示的頁面的總數(shù)。分頁程序管理數(shù)據(jù)檢索和分頁所需的任何邏輯。

selectcommand 屬性設(shè)置獲取數(shù)據(jù)所用的命令文本。connectionstring 屬性定義數(shù)據(jù)庫的名稱和位置以及連接憑據(jù)。執(zhí)行查詢時(shí)采用的方式取決于 pagingmode 屬性的值。該屬性的可能值為與其同名的 pagingmode 枚舉的值 - cached 和 noncached。如果選擇 cached 選項(xiàng),則將使用數(shù)據(jù)適配器和 datatable 對(duì)象檢索整個(gè)結(jié)果集。可以選擇將結(jié)果集放置在 asp.net 的 cache 對(duì)象中,該結(jié)果集可以重復(fù)使用直到過期。如果選擇 noncached 選項(xiàng),則查詢只檢索適合當(dāng)前頁面的記錄。這時(shí),asp.net 的 cache 中不放置任何數(shù)據(jù)。noncached 模式與 datagrid 控件的自定義分頁模式幾乎相同。

下表列出 sqlpager 控件的全部編程接口。

表 1:sqlpager 控件的編程接口

名稱 類型 說明
cacheduration 屬性 指示數(shù)據(jù)在 asp.net 的緩存中保留的秒數(shù)。只用于 cached 模式。默認(rèn)值為 60 秒。
connectionstring 屬性 用來訪問所選擇的 sql server 數(shù)據(jù)庫的連接字符串。
controltopaginate 屬性 同一個(gè) .aspx 頁面中的控件 id,它將顯示分頁程序檢索的記錄頁面。這是合作者控件。
currentpageindex 屬性 獲取并設(shè)置基于 0 的頁面索引。
itemsperpage 屬性 獲取并設(shè)置每頁要顯示的記錄數(shù)量。默認(rèn)值為每頁顯示 10 個(gè)項(xiàng)目。
pagerstyle 屬性 該值指示分頁程序用戶界面的樣式。它可以為 pagerstyle 枚舉值:nextprev 和 numericpages 之一。在 nextprev 模式中,將顯示 vcr 式的按鈕,來轉(zhuǎn)到第一頁、上一頁、下一頁和最后一頁。而在 numericpages 模式中,將顯示一個(gè)下拉列表,列出所有可用頁面的索引。
pagingmode 屬性 該值指示檢索數(shù)據(jù)的方式。它可以為 pagingmode 枚舉值:cached 和 noncached 之一。如果為 cached,則將使用數(shù)據(jù)適配器,且整個(gè)結(jié)果集將臨時(shí)放置在 asp.net 緩存中。如果為 noncached,則只檢索當(dāng)前頁面中的記錄。在這種情況下,不進(jìn)行緩存。
selectcommand 屬性 用來進(jìn)行查詢的命令文本。最好為 select-from-where 形式。不支持 order by 子句。排序是通過 sortfield 屬性另外指定的。
sortfield 屬性 用來排序的字段的名稱。此字段用于為查詢提供動(dòng)態(tài)的 order by 子句。排序是由 sql server 執(zhí)行的。
clearcache 方法 刪除存儲(chǔ)在 asp.net 緩存中的任何數(shù)據(jù)。
pageindexchanged 事件 默認(rèn)事件,當(dāng)分頁程序移動(dòng)到另一個(gè)頁面時(shí)發(fā)生。事件的數(shù)據(jù)結(jié)構(gòu)為 pagechangedeventargs 類,包含舊頁面和新頁面的索引。

由于 sqlpager 控件繼承了 webcontrol,因此它也具有很多與 ui 相關(guān)的屬性來管理字體、邊框和顏色。

生成 sqlpager 控件

將作為復(fù)合控件來生成 sqlpager 控件并讓其繼承 webcontrol 類。復(fù)合控件是 asp.net 服務(wù)器控件所特有的,它是由一個(gè)或多個(gè)服務(wù)器控件組成。

public class sqlpager : webcontrol, inamingcontainer
{ ... }

除非生成完全自定義的控件或擴(kuò)展現(xiàn)有的控件,否則,創(chuàng)建新控件時(shí),大多數(shù)時(shí)間實(shí)際上是在生成復(fù)合控件。要?jiǎng)?chuàng)建 sqlpager,組合一個(gè) table 控件,并根據(jù)分頁程序的樣式,組合幾個(gè) linkbutton 控件或者一個(gè) dropdownlist 控件。

生成復(fù)合控件時(shí),需要注意幾條原則。首先,需要覆蓋 createchildcontrols protected 方法。createchildcontrols 方法是從 control 繼承來的,當(dāng)服務(wù)器控件為了顯示而要?jiǎng)?chuàng)建子控件時(shí)或在返回后,將調(diào)用此方法。

protected override void createchildcontrols()
{
// 清除現(xiàn)有的子控件及其 viewstate
controls.clear();
clearchildviewstate();

// 生成控件樹
buildcontrolhierarchy();
}

覆蓋此方法時(shí),需要執(zhí)行幾項(xiàng)重要的操作。創(chuàng)建并初始化任何所需的子控件實(shí)例并將它們添加到父控件的 controls 集合中。但是,生成新控件樹之前,應(yīng)該刪除任何現(xiàn)有的子控件并清除子控件可能留下的任何 viewstate 信息。

復(fù)合組件還需要實(shí)現(xiàn) inamingcontainer 接口,以便 asp.net 運(yùn)行時(shí)可以為其創(chuàng)建一個(gè)新的命名范圍。這就確保了復(fù)合控件中的所有控件都具有唯一的名稱。這還將確保能夠自動(dòng)處理子控件的返回?cái)?shù)據(jù)。

對(duì)于 sqlpager 控件來說,成為命名容器非常重要。事實(shí)上,sqlpager 包含一些 linkbutton 控件,并且需要獲取并處理其單擊事件以便導(dǎo)航頁面。正如 asp.net 頁面中的任何其他控件一樣,linkbutton 也被賦予了一個(gè) id,用于標(biāo)識(shí)處理返回事件的控件。

處理返回事件時(shí),asp.net 運(yùn)行時(shí)試圖查找事件的目標(biāo) id 與主窗體的任何直接子控件之間是否存在匹配關(guān)系。linkbutton 是分頁程序的子控件,因此不能運(yùn)行其服務(wù)器端的代碼。這是否意味著只有窗體的直接子控件才能啟動(dòng)并處理服務(wù)器事件?當(dāng)然不是,只要您使用命名容器。

通過使 sqlpager 控件實(shí)現(xiàn) inamingcontainer 接口,可以將嵌入式鏈接按鈕的實(shí)際 id 從“first”更改為“sqlpager1:first”。當(dāng)用戶單擊以查看新頁面時(shí),返回事件將 sqlpager1:first 作為目標(biāo)控件。實(shí)際上,運(yùn)行時(shí)用來識(shí)別目標(biāo)控件的算法比上面介紹的要復(fù)雜一些。運(yùn)行時(shí)將事件目標(biāo)控件的名稱看作是用冒號(hào)分隔開的字符串。實(shí)際上,這種匹配是在窗體的子控件和用冒號(hào)分隔開的字符串(如 sqlpager1:first)的第一個(gè)標(biāo)記之間進(jìn)行的。由于分頁程序是窗體的子控件,因此匹配時(shí)會(huì)成功,分頁程序獲取單擊事件。如果您認(rèn)為這種解釋不夠充分或者令人費(fèi)解,只要下載 sqlpager 控件的源代碼,刪除 inamingcontainer 標(biāo)記接口并進(jìn)行重新編譯即可。您將看到分頁程序能夠返回,但不能內(nèi)部處理單擊事件。

inamingcontainer 接口是一個(gè)不具備方法的標(biāo)記接口,其實(shí)現(xiàn)只需在類聲明中指定名稱即可,無需進(jìn)行任何其他操作。

復(fù)合控件的另一個(gè)重要方面是,它們通常不需要自定義邏輯來進(jìn)行顯示。復(fù)合控件的顯示遵循其組成控件的顯示。生成復(fù)合控件時(shí),通常無需覆蓋 render 方法。

控件的 sqlpager 樹由一個(gè)包含兩個(gè)單元格的單行表格組成。該表格繼承了分頁程序的大部分可視設(shè)置,如前景顏色和背景顏色、邊框、字體信息和寬度等。第一個(gè)單元格包含導(dǎo)航條,其結(jié)構(gòu)取決于 pagerstyle 屬性的值。如果分頁程序的樣式為 nextprev,則導(dǎo)航條將由四個(gè) vcr 式的按鈕組成。否則,它將由一個(gè)下拉列表組成。

private void buildcontrolhierarchy()
{
// 生成環(huán)境表格(一行,兩個(gè)單元格)
table t = new table();
t.font.name = this.font.name;
t.font.size = this.font.size;
t.borderstyle = this.borderstyle;
t.borderwidth = this.borderwidth;
t.bordercolor = this.bordercolor;
t.width = this.width;
t.height = this.height;
t.backcolor = this.backcolor;
t.forecolor = this.forecolor;

// 生成表格中的行
tablerow row = new tablerow();
t.rows.add(row);

// 生成帶有導(dǎo)航條的單元格
tablecell cellnavbar = new tablecell();
if (pagerstyle == this.pagerstyle.nextprev)
buildnextprevui(cellnavbar);
else
buildnumericpagesui(cellnavbar);
row.cells.add(cellnavbar);

// 生成帶有頁面索引的單元格
tablecell cellpagedesc = new tablecell();
cellpagedesc.horizontalalign = horizontalalign.right;
buildcurrentpage(cellpagedesc);
row.cells.add(cellpagedesc);

// 將表格添加到控件樹
this.controls.add(t);
}

將各個(gè)控件添加到正確的 controls 集合對(duì)于分頁程序的正確顯示極其重要。最外層的表格必須添加到分頁程序的 controls 集合中。鏈接按鈕和下拉列表必須添加到相應(yīng)表格單元格的 controls 集合中。

下面給出了用來生成鏈接按鈕導(dǎo)航條的代碼。每個(gè)按鈕都顯示有一個(gè) webdings 字符,可以根據(jù)需要禁用,并被綁定到內(nèi)部的 click 事件處理程序。

private void buildnextprevui(tablecell cell)
{
bool isvalidpage = ((currentpageindex >=0) &&
(currentpageindex <= totalpages-1));
bool canmoveback = (currentpageindex>0);
bool canmoveforward = (currentpageindex<totalpages-1);

// 顯示 << 按鈕
linkbutton first = new linkbutton();
first.id = "first";
first.click += new eventhandler(first_click);
first.font.name = "webdings";
first.font.size = fontunit.medium;
first.forecolor = forecolor;
first.tooltip = "第一頁";
first.text = "7";
first.enabled = isvalidpage && canmoveback;
cell.controls.add(first);
:
}

分頁程序的另一種樣式(在下拉列表中列出數(shù)字頁面)的生成方法如下所示:

private void buildnumericpagesui(tablecell cell)
{
// 顯示一個(gè)下拉列表
dropdownlist pagelist = new dropdownlist();
pagelist.id = "pagelist";
pagelist.autopostback = true;
pagelist.selectedindexchanged += new eventhandler(pagelist_click);
pagelist.font.name = this.font.name;
pagelist.font.size = font.size;
pagelist.forecolor = forecolor;

if (totalpages <=0 || currentpageindex == -1)
{
pagelist.items.add("no pages");
pagelist.enabled = false;
pagelist.selectedindex = 0;
}
else // 填充列表
{
for(int i=1; i<=totalpages; i++)
{
listitem item = new listitem(i.tostring(), (i-1).tostring());
pagelist.items.add(item);
}
pagelist.selectedindex = currentpageindex;
}
}

所有事件處理程序(click 和 selectedindexchanged)最終都會(huì)更改當(dāng)前顯示的頁面。這兩種方法都會(huì)調(diào)用一個(gè)公用的 private 方法 gotopage。

private void first_click(object sender, eventargs e)
{
gotopage(0);
}
private void pagelist_click(object sender, eventargs e)
{
dropdownlist pagelist = (dropdownlist) sender;
int pageindex = convert.toint32(pagelist.selecteditem.value);
gotopage(pageindex);
}
private void gotopage(int pageindex)
{
// 準(zhǔn)備事件數(shù)據(jù)
pagechangedeventargs e = new pagechangedeventargs();
e.oldpageindex = currentpageindex;
e.newpageindex = pageindex;

// 更新當(dāng)前的索引
currentpageindex = pageindex;

// 啟動(dòng)頁面更改事件
onpageindexchanged(e);

// 綁定新數(shù)據(jù)
databind();
}

其他導(dǎo)航按鈕的處理程序與 first_click 的區(qū)別僅在于它們傳遞給 gotopage 方法的頁碼不同。gotopage 方法負(fù)責(zé)處理 pageindexchanged 事件,并負(fù)責(zé)啟動(dòng)數(shù)據(jù)綁定過程。它準(zhǔn)備事件數(shù)據(jù)(舊頁面和新頁面索引)并觸發(fā)事件。gotopage 被定義為 private,但是可以使用 currentpageindex 屬性通過編程的方式更改顯示的頁面。

public int currentpageindex
{
get {return convert.toint32(viewstate["currentpageindex"]);}
set {viewstate["currentpageindex"] = value;}
}

與表 1 中所列的所有屬性一樣,currentpageindex 屬性的實(shí)現(xiàn)方法也相當(dāng)簡(jiǎn)單。它將其內(nèi)容保存到 viewstate 中并從中進(jìn)行還原。在數(shù)據(jù)綁定過程中,會(huì)驗(yàn)證和使用頁面索引。

數(shù)據(jù)綁定過程

databind 方法是所有 asp.net 控件公用的,對(duì)于數(shù)據(jù)綁定控件來說,它將觸發(fā)用戶界面的刷新以反映新數(shù)據(jù)。sqlpager 控件根據(jù) selectcommand 和 connectionstring 屬性的值,使用此方法啟動(dòng)數(shù)據(jù)檢索操作。不言而喻,如果這些屬性中的任何一個(gè)為空,該過程將終止。同樣,如果合作者控件不存在,數(shù)據(jù)綁定過程將被取消。要查找合作者控件,databind 方法使用 page 類中的 findcontrol 函數(shù)。由此可見,合作者控件必須為主窗體的直接子控件。

進(jìn)行分頁顯示的控件不能為任意的 asp.net 服務(wù)器控件。它必須為列表控件或基本數(shù)據(jù)列表控件。更一般來說,合作者控件必須具有 datasource 屬性并實(shí)現(xiàn) databind 方法。可能進(jìn)行分頁的控件實(shí)際上只需要滿足這些要求。microsoft&reg; .net framework 中所有繼承 listcontrol 或 basedatalist 的控件都滿足第一個(gè)要求;而所有 web 控件通過設(shè)計(jì)都滿足 databind 要求。使用當(dāng)前的實(shí)現(xiàn)方法,無法使用 sqlpager 控件來對(duì) repeater 進(jìn)行分頁。repeater 與合作者控件 datalist 和 datagrid 不同,不繼承 basedatalist,也不提供列表控件的功能。下表列出了可以使用 sqlpager 進(jìn)行分頁的控件。

表 2:可以由 sqlpager 控件進(jìn)行分頁的數(shù)據(jù)綁定控件

控件 說明
checkboxlist 從 listcontrol 派生而來,顯示為復(fù)選框列表。
dropdownlist 從 listcontrol 派生而來,顯示為字符串下拉列表。
listbox 從 listcontrol 派生而來,顯示為字符串可滾動(dòng)列表。
radiobuttonlist 從 listcontrol 派生而來,顯示為單選按鈕列表。
datalist 從 basedatalist 派生而來,顯示為模板化數(shù)據(jù)項(xiàng)目列表。
datagrid 從 basedatalist 派生而來,顯示為數(shù)據(jù)項(xiàng)目的表格網(wǎng)格。datagrid 是唯一一個(gè)內(nèi)置有功能強(qiáng)大的分頁引擎的 asp.net 控件。

以下代碼說明由 sqlpager 控件實(shí)現(xiàn)的數(shù)據(jù)綁定過程。

public override void databind()
{
// 啟動(dòng)數(shù)據(jù)綁定事件
base.databinding();

// 數(shù)據(jù)綁定后必須重新創(chuàng)建控件
childcontrolscreated = false;

// 確保控件存在且為列表控件
_controltopaginate = page.findcontrol(controltopaginate);
if (_controltopaginate == null)
return;
if (!(_controltopaginate is basedatalist ||
_controltopaginate is listcontrol))
return;

// 確保具有足夠的連接信息并指定查詢
if (connectionstring == "" || selectcommand == "")
return;

// 獲取數(shù)據(jù)
if (pagingmode == pagingmode.cached)
fetchalldata();
else
fetchpagedata();

// 將數(shù)據(jù)綁定到合作者控件
basedatalist basedatalistcontrol = null;
listcontrol listcontrol = null;
if (_controltopaginate is basedatalist)
{
basedatalistcontrol = (basedatalist) _controltopaginate;
basedatalistcontrol.datasource = _datasource;
basedatalistcontrol.databind();
return;
}
if (_controltopaginate is listcontrol)
{
listcontrol = (listcontrol) _controltopaginate;
listcontrol.items.clear();
listcontrol.datasource = _datasource;
listcontrol.databind();
return;
}
}

根據(jù) pagingmode 屬性的值調(diào)用不同的獲取例程。在任何情況下,結(jié)果集都綁定到 pageddatasource 類的實(shí)例上。此類提供了一些用來對(duì)數(shù)據(jù)進(jìn)行分頁的功能。特別是,當(dāng)整個(gè)數(shù)據(jù)集都存儲(chǔ)在緩存中時(shí),該類將自動(dòng)檢索當(dāng)前頁面的記錄并返回布爾值,來提供有關(guān)第一頁和最后一頁的信息。稍后將回來介紹此類的內(nèi)部結(jié)構(gòu)。在上述列表中,幫助程序的 pageddatasource 對(duì)象是由 _datasource 變量表示的。

然后,sqlpager 控件經(jīng)過計(jì)算得出合作者控件的類型,并將 pageddatasource 對(duì)象的內(nèi)容綁定到合作者控件的 datasource 屬性。

有時(shí),上述的 databind 方法還將 childcontrolscreated 屬性重新設(shè)置為 false。那么,為什么要這樣做呢?

當(dāng)包含分頁程序的頁面返回時(shí),所有控件都要重新創(chuàng)建;分頁程序也不例外。通常情況下,所有控件及其子控件都是在準(zhǔn)備顯示頁面之前創(chuàng)建的。在每個(gè)控件接收到 onprerender 通知之前的一瞬間,protected ensurechildcontrols 方法將被調(diào)用,這樣,每個(gè)控件都可以生成自己的控件樹。此事件發(fā)生后,數(shù)據(jù)綁定過程完成,新數(shù)據(jù)已存儲(chǔ)到緩存中。

但是,當(dāng)由于單擊分頁程序的一個(gè)組成控件而使頁面返回時(shí)(即用戶單擊以更改頁面),就會(huì)生成分頁程序的控件樹,這時(shí)遠(yuǎn)未達(dá)到顯示階段。特別是,當(dāng)處理相關(guān)的服務(wù)器端事件時(shí),就必須生成控件樹,因而是在數(shù)據(jù)綁定開始之前生成控件樹。困難在于,數(shù)據(jù)綁定將修改頁面索引,而這必須反映在用戶界面中。如果不采取某些對(duì)策的話,當(dāng)用戶切換到另一頁時(shí),分頁程序中的頁面索引將不會(huì)被刷新。

有各種方法可以解決此問題,但重要的是要弄清楚問題及其真正的原因。您可以避免生成控件樹,并在 render 方法中生成所有輸出。另外,您還可以修改樹中受數(shù)據(jù)綁定更改影響的部分。本文選擇了第三種方法,這種方法需要較少的代碼,而且,不管控件的用戶界面的哪個(gè)部分受到數(shù)據(jù)綁定更改的影響,都能夠解決問題。通過將 childcontrolscreated 屬性設(shè)置為 false,可以使以前創(chuàng)建的任何控件樹無效。這樣,在顯示之前將重新創(chuàng)建控件樹。

分頁引擎

sqlpager 控件支持兩種檢索數(shù)據(jù)的方法 - 緩存和非緩存。如果采用前者,選擇命令原樣執(zhí)行,整個(gè)結(jié)果集將綁定到在內(nèi)部進(jìn)行分頁的數(shù)據(jù)源對(duì)象上。pageddatasource 對(duì)象將自動(dòng)返回適合特定頁面的記錄。pageddatasource 類也是在 datagrid 默認(rèn)分頁機(jī)制背后運(yùn)行的系統(tǒng)組件。

檢索所有記錄但只顯示適合頁面的幾個(gè)記錄,這通常不是一種明智的方法。由于 web 應(yīng)用程序的無狀態(tài)特性,事實(shí)上,每次請(qǐng)求頁面時(shí)都可能運(yùn)行大量的查詢。要使操作有效,采用緩存的數(shù)據(jù)檢索方法必須依賴于某種緩存對(duì)象,asp.net 的 cache 對(duì)象就是一個(gè)很好的候選對(duì)象。緩存技術(shù)的使用加快了應(yīng)用程序的運(yùn)行速度,但其提供的數(shù)據(jù)快照不能反映最新的更改。另外,它需要使用 web 服務(wù)器上的較多內(nèi)存。而且荒謬的是,如果大量的數(shù)據(jù)按會(huì)話緩存的話,甚至可能造成很大的問題。cache 容器對(duì)于應(yīng)用程序來說是全局的;如果數(shù)據(jù)按會(huì)話存儲(chǔ)在其中,還需生成會(huì)話特有的項(xiàng)目名稱。

cache 對(duì)象的上面是完全支持過期策略的。換句話說,存儲(chǔ)在緩存中的數(shù)據(jù)在一段時(shí)間后可以自動(dòng)被釋放。以下代碼說明 sqlpager 類中用來獲取數(shù)據(jù)并將其存儲(chǔ)在緩存中的一個(gè) private 方法。

private void fetchalldata()
{
// 在 asp.net cache 中查找數(shù)據(jù)
datatable data;
data = (datatable) page.cache[cachekeyname];
if (data == null)
{
// 使用 order-by 信息修改 selectcommand
adjustselectcommand(true);

// 如果數(shù)據(jù)過期或從未被獲取,則轉(zhuǎn)到數(shù)據(jù)庫
sqldataadapter adapter = new sqldataadapter(selectcommand,
connectionstring);
data = new datatable();
adapter.fill(data);
page.cache.insert(cachekeyname, data, null,
datetime.now.addseconds(cacheduration),
system.web.caching.cache.noslidingexpiration);
}

// 配置分頁的數(shù)據(jù)源組件
if (_datasource == null)
_datasource = new pageddatasource();
_datasource.datasource = data.defaultview;
_datasource.allowpaging = true;
_datasource.pagesize = itemsperpage;
totalpages = _datasource.pagecount;

// 確保頁面索引有效
validatepageindex();
if (currentpageindex == -1)
{
_datasource = null;
return;
}

// 選擇要查看的頁面
_datasource.currentpageindex = currentpageindex;
}

控件和請(qǐng)求的緩存項(xiàng)目名稱是唯一的。它包括頁面的 url 和控件的 id。在指定的時(shí)間(以秒計(jì)算)內(nèi),數(shù)據(jù)被綁定到緩存。要使項(xiàng)目過期,必須使用 cache.insert 方法。以下較簡(jiǎn)單的代碼將項(xiàng)目添加到緩存,但不包括任何過期策略。

page.cache[cachekeyname] = data;

pageddatasource 對(duì)象通過其 datasource 屬性獲取要進(jìn)行分頁的數(shù)據(jù)。值得注意的是,pageddatasource 類的 datasource 屬性只接受 ienumerable 對(duì)象。datatable 不滿足此要求,這就是為什么采取 defaultview 屬性的原因。

selectcommand 屬性確定在 sql server 數(shù)據(jù)庫上運(yùn)行的查詢。此字符串最好為 select-from-where 形式。不支持 order by 子句,如果指定了該子句,將被刪除。這正是 adjustselectcommand 方法所做的。使用 sortfield 屬性可以指定任何排序信息。adjustselectcommand 方法本身將根據(jù) sortfield 的值添加一個(gè)正確的 order by 子句。這樣做有什么原因嗎?

當(dāng)分頁程序以 noncached 模式工作時(shí),原始的查詢將被修改以確保只檢索當(dāng)前頁面的記錄。在 sql server 上執(zhí)行的實(shí)際查詢文本將采取以下形式。

select * from
(select top itemsperpage * from
(select top itemsperpage*currentpageindex * from
(selectcommand) as t0
order by sortfield asc) as t1
order by sortfield desc) as t2
order by sortfield

該查詢彌補(bǔ)了 sql server 2000 中 rownum 子句的缺陷,并且對(duì)記錄進(jìn)行重新排序,使得只有 x 項(xiàng)目的“第 n 個(gè)”塊經(jīng)過正確排序后返回。您可以指定基礎(chǔ)查詢,分頁程序?qū)⑺纸鉃槎鄠€(gè)較小的頁面。只有適合某個(gè)頁面的記錄被返回。正如您看到的那樣,除了查詢命令以外,上述查詢需要處理排序字段。這就是為什么另外添加了 sortfield 屬性。此代碼的唯一缺陷是默認(rèn)情況為升序排序。通過使 asc/desc 關(guān)鍵字參數(shù)化,可以使此代碼真的非常完美:

private void fetchpagedata()
{
// 需要經(jīng)過驗(yàn)證的頁面索引來獲取數(shù)據(jù)。
// 還需要實(shí)際的頁數(shù)來驗(yàn)證頁面索引。
adjustselectcommand(false);
virtualrecordcount countinfo = calculatevirtualrecordcount();
totalpages = countinfo.pagecount;

// 驗(yàn)證頁碼(確保 currentpageindex 有效或?yàn)椤?1”)
validatepageindex();
if (currentpageindex == -1)
return;

// 準(zhǔn)備并運(yùn)行命令
sqlcommand cmd = preparecommand(countinfo);
if (cmd == null)
return;
sqldataadapter adapter = new sqldataadapter(cmd);
datatable data = new datatable();
adapter.fill(data);

// 配置分頁的數(shù)據(jù)源組件
if (_datasource == null)
_datasource = new pageddatasource();
_datasource.allowcustompaging = true;
_datasource.allowpaging = true;
_datasource.currentpageindex = 0;
_datasource.pagesize = itemsperpage;
_datasource.virtualcount = countinfo.recordcount;
_datasource.datasource = data.defaultview;
}

在 noncached 模式中,pageddatasource 對(duì)象并不提供整個(gè)數(shù)據(jù)源,因此不能計(jì)算出要進(jìn)行分頁的總頁數(shù)。進(jìn)而必須對(duì) allowcustompaging 屬性進(jìn)行標(biāo)記,并提供數(shù)據(jù)源中的實(shí)際記錄數(shù)量。通常,實(shí)際數(shù)量是使用 select count(*) 查詢進(jìn)行檢索的。此模型與 datagrid 的自定義分頁幾乎相同。此外,pageddatasource 對(duì)象中選擇的當(dāng)前頁面索引通常為 0,因?yàn)閷?shí)際上已經(jīng)存儲(chǔ)了一頁記錄。

sqlpager 控件的實(shí)現(xiàn)方法就介紹到這里,下面我們介紹一下它的使用方法。

使用 sqlpager 控件

假設(shè)存在一個(gè)包含 listbox 控件的示例頁面。要使用分頁程序,請(qǐng)確保 .aspx 頁面正確地注冊(cè)了該控件的程序集。

<%@ register tagprefix="expo" namespace="devcenter" assembly="sqlpager" %>

控件的標(biāo)記取決于實(shí)際設(shè)置的屬性。以下標(biāo)記是一個(gè)合理的示例:

<asp:listbox runat="server" id="listbox1"
width="300px" height="168px"
datatextfield="companyname" />
<br>
<expo:sqlpager runat="server" id="sqlpager1" width="300px"
controltopaginate="listbox1"
selectcommand="select customerid, companyname from customers"
connectionstring="server=localhost;database=northwind;uid=..."
sortkeyfield="companyname" />
<br>
<asp:button runat="server" id="loadfirst1" text="加載第一頁" />

除了分頁程序以外,頁面還包含一個(gè)列表框和一個(gè)按鈕。列表框?qū)@示每個(gè)頁面的內(nèi)容;按鈕只用于首次填充列表框。該按鈕具有一個(gè)單擊事件處理程序,定義如下。

private void loadfirst1_click(object sender, eventargs e) {
sqlpager1.currentpageindex = 0;
sqlpager1.databind();
}

圖 2 顯示操作中的頁面。



圖 2:與 listbox 控件協(xié)同工作的 sqlpager 控件。

使用 datalist 控件可以生成一個(gè)更有意思的示例。目標(biāo)是使用分頁程序?yàn)g覽每個(gè) northwind 職員的個(gè)人記錄。該 datalist 如以下列表所示。

<asp:datalist runat="server" id="datalist1" width="300px"
font-names="verdana" font-size="8pt">
<itemtemplate>
<table bgcolor="#f0f0f0" style="font-family:verdana;font-size:8pt;">
<tr><td valign="top">
<b><%# databinder.eval(container.dataitem, "lastname") + ", " +
databinder.eval(container.dataitem, "firstname") %></b></td></tr>

<tr><td>
<span style="color:blue;"><i>
<%# databinder.eval(container.dataitem, "title")%></i></span>
<p><img style="float:right;" src='image.aspx?
id=<%# databinder.eval(container.dataitem, "employeeid")%>' />
<%# databinder.eval(container.dataitem, "notes") %></td></tr>
</table>
</itemtemplate>
</asp:datalist>

表格的第一行顯示職員的姓名和職務(wù),然后是相片,相片周圍是注釋。相片是使用特定的 .aspx 頁面檢索的,返回從數(shù)據(jù)庫中獲取的 jpeg 數(shù)據(jù)。

分頁程序可以放置在頁面中的任何位置。本例中將它放置在合作者 datalist 控件上方并緊挨著合作者控件。



圖 3:sqlpager 對(duì) datalist 控件進(jìn)行分頁

將 sqlpager 控件與 datagrid 控件一起使用有意義嗎?這要視情況而定。datagrid 已經(jīng)與一個(gè)基于本文中使用的 pageddatasource 對(duì)象的嵌入式分頁引擎一起工作。因此,如果您需要對(duì)以表格格式顯示的單個(gè)記錄集合進(jìn)行分頁時(shí),就無需使用 sqlpager。但是,對(duì)于重要/復(fù)雜的方案,將這兩個(gè)控件一起使用并不是一種牽強(qiáng)的想法。例如,如果您要向前一個(gè)屏幕快照添加一個(gè) datagrid 來顯示由職員管理的訂單,則可以在同一格頁面上放置兩個(gè)相關(guān)的分頁引擎,一個(gè)要對(duì)職員進(jìn)行分頁,另一個(gè)用于滾動(dòng)相關(guān)訂單。

小結(jié)

不管您要生成哪種類型的應(yīng)用程序(web 應(yīng)用程序、microsoft&reg; windows&reg; 應(yīng)用程序或 web 服務(wù)),您都很難下載和緩存要顯示的整個(gè)數(shù)據(jù)源。有時(shí),測(cè)試環(huán)境會(huì)使您相信這種解決方案真是太棒了,非常可取。但是測(cè)試環(huán)境也會(huì)誤導(dǎo)。數(shù)據(jù)源的大小非常關(guān)鍵,應(yīng)用程序的規(guī)模越大,數(shù)據(jù)源的大小越關(guān)鍵。

在 asp.net 中,只有 datagrid 控件具有內(nèi)置的分頁功能。但是,分頁引擎是由相當(dāng)樣板化的代碼實(shí)現(xiàn)的,只要進(jìn)行少量的處理,就可以進(jìn)行推廣并用于多個(gè)不同的控件。本文介紹的 sqlpager 控件就實(shí)現(xiàn)了此目標(biāo)。它關(guān)注下載數(shù)據(jù),將數(shù)據(jù)分成多個(gè)頁面并通過合作者控件顯示。該控件可以檢索并緩存整個(gè)數(shù)據(jù)集,或者僅在 sql server 中查詢要在選定的頁面中顯示的幾個(gè)記錄。我說的是 sql server,這是另一個(gè)重點(diǎn)。sqlpager 只能用于 sql server,不能用于通過 ole db 或 odbc 來檢索數(shù)據(jù)。也不能使用它訪問 oracle 或 db2 存檔。

要生成真正的通用 sql 分頁程序組件,應(yīng)該形成數(shù)據(jù)訪問層,并生成一種能夠使用相應(yīng)的數(shù)據(jù)提供程序創(chuàng)建連接、命令和適配器的工廠類。此外,要注意為各種 sql 源設(shè)置一個(gè)分頁引擎是最糟糕的做法。這里介紹的方法只適用于 sql server 7.0 和更高版本。top 子句在不同的環(huán)境下具有不同的特性。使用服務(wù)器游標(biāo)和臨時(shí)表格,它可適用于較大范圍的 dbms 系統(tǒng)。但那將使代碼更為復(fù)雜。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 手游| 临沧市| 牡丹江市| 镇雄县| 汉川市| 阳泉市| 唐河县| 玉门市| 道孚县| 咸宁市| 句容市| 景东| 韶关市| 深水埗区| 永仁县| 乌兰县| 黎平县| 屏东市| 峡江县| 睢宁县| 屯留县| 沁水县| 哈巴河县| 镇安县| 阳西县| 孝义市| 鹿邑县| 福鼎市| 凤台县| 双鸭山市| 攀枝花市| 江津市| 闸北区| 陇南市| 阿拉善右旗| 安义县| 梅河口市| 航空| 澜沧| 运城市| 固镇县|