iT邦幫忙

2021 iThome 鐵人賽

DAY 18
1
Modern Web

ASP.NET Web Forms 入門 - 30天建立遊艇網頁專案後端及後台功能 C#系列 第 18

Day 18 - Using ASCX File to Create Pagination Function with ASP.NET Web Forms C# 建立使用者控制項 - 製作分頁功能

=x= 🌵 Web Forms 使用者控制項-製作分頁功能。


Pagination 分頁功能介紹 :

📌 遊艇網頁的 NEWS 新聞列表頁底部的分頁功能,算是專案的後端功能裡比較難的部分,製作時會使用到 "Web Forms 使用者控制項" 來自製控制項,製作完畢後拖拉進頁面就可以使用,新聞列表頁使用時也會用到比較複雜的 SQL 語法來搭配,分頁控制項包含資料總數的呈現,相同資料量但因為每頁呈現的數量不同,分頁號碼項目會因為頁數多寡而有不同的效果,可以參考下方截圖 :

1. 分頁數較少時,正常顯示模式 : 左側紅字為總資料數。(每頁4筆資料)

https://ithelp.ithome.com.tw/upload/images/20211002/20139487N01jZB4MFs.jpg

2. 分頁數較多時,隱藏顯示模式,當前頁面靠前幾頁時 : 只會隱藏右側。(每頁2筆資料)

https://ithelp.ithome.com.tw/upload/images/20211002/20139487ekx85ndfF7.jpg

3. 分頁數較多時,隱藏顯示模式,當前頁面靠中間頁時 : 會同時隱藏左右側。(每頁2筆資料)

https://ithelp.ithome.com.tw/upload/images/20211002/20139487QnELTFa0UI.jpg

4. 分頁數較多時,隱藏顯示模式,當前頁面靠後幾頁時 : 只會隱藏左側。(每頁2筆資料)

https://ithelp.ithome.com.tw/upload/images/20211002/20139487mJwT6LA9AO.jpg



Pagination 分頁功能實作 :

1. 於方案總管的專案名稱上右鍵加入新增項目 "Web Forms 使用者控制項"

https://ithelp.ithome.com.tw/upload/images/20211002/20139487EdJTR4OKlo.jpg


2. 於新增的 .ascx 頁面加入 asp:Literal 控制項如下

<asp:Literal ID="litPage" runat="server"></asp:Literal>


3. 於 .ascx.cs 開始撰寫分頁控制項邏輯,先建立控制項屬性接收值的類型

//設定自製控制項屬性接收值的類型
public int totalItems { get; set; } //總共幾筆資料
public int limit { get; set; } //一頁幾筆資料
public string targetPage { get; set; } //作用頁面完整網頁名稱


4. 接著建立用正規式判斷是否是數字 IsNumber() 方法如下

#region "用正規式判斷是否為數字"
/// <summary>
/// 用正規式判斷是否為數字
/// </summary>
/// <param name="inputData">輸入字串</param>
/// <returns>bool</returns>
bool IsNumber(string inputData)
{
    return System.Text.RegularExpressions.Regex.IsMatch(inputData, "^[0-9]+$");
}
#endregion
  • 🌵 #region 自訂折疊程式碼區域,可任意選取想折疊的區域後右鍵加入,參考如下

https://ithelp.ithome.com.tw/upload/images/20211002/20139487MsXHTEGv5F.jpg


5. 建立渲染控制項 showPageControls() 方法如下

public void showPageControls()
{
    litPage.Text = ""; //清空分頁控制項
    int page = 1; //預設第1頁
    //如果網址有傳值
    if (!string.IsNullOrEmpty(Request["page"])) {
        //傳值為數字
        if (IsNumber(Request["page"])) {
            page = Convert.ToInt16(Request["page"]); //修改當前頁碼
        }
    }
    if (totalItems == 0) {
        return;
    }
    if (limit == 0) {
        return;
    }
    //確認當前頁面檔案名稱非 null 在 ?? 左側非 null 則不變,左側是 null 則傳回右側結果 
    targetPage = targetPage ?? System.IO.Path.GetFileName(Request.PhysicalPath);
    //渲染分頁控制項 //鄰近頁 adjacents 參數不建議設太大,可能導致換行
    litPage.Text = getPaginationString(page, totalItems, limit, 2, targetPage);
}


6. 最後,建立產生分頁選項的邏輯 getPaginationString() 方法如下

#region "產生分頁控制項"
/// <summary>
/// 產生分頁控制項
/// </summary>
/// <param name="page">目前第幾頁</param>
/// <param name="totalItems">共有幾筆</param>
/// <param name="limit">一頁幾筆資料</param>
/// <param name="adjacents">鄰近頁左右各幾筆,兩側隱藏時只顯示當前頁左右各幾頁,不可為0,值設太大可能導致換行,建議值為2</param>
/// <param name="targetPage">當前頁面檔案名稱,例:index.aspx</param>
/// <returns>回傳HTML標籤字串</returns>
public static string getPaginationString(int page, int totalItems, int limit, int adjacents, string targetPage)
{
    //判斷預設網頁有無帶有傳值用,如果有出現 ? 表示已有傳參數就在後面加 & 加掛,如無則補加 ? //預設在檔名後加問號
    targetPage = targetPage.IndexOf('?') != -1 ? targetPage + "&" : targetPage + "?";
    //前一頁 = 目前頁面-1
    int prev = page-1;
    //下一頁 = 目前頁面+1
    int nextPage = page+1;
    //總頁數數值 = 總資料筆數/每頁幾筆
    Double value = Convert.ToDouble((decimal)totalItems / limit);
    //最末頁(總頁數) = 總頁數數值無條件進位成整數
    int lastpage = Convert.ToInt16(Math.Ceiling(value));
    //倒數第二頁 = 最末頁-1
    int secondLast = lastpage-1;
    //邏輯判斷共用參數
    int commonParameter = 3+(adjacents*2); //不可修改:"3"代表當前頁+首或末兩頁,"2"代表左右側頁
    //建立分頁 HTML 字串邏輯
    StringBuilder paginationBuilder = new StringBuilder();
    //超過1頁才顯示分頁控制項
    if (lastpage > 1) {
        //共計幾筆資料 HTML
        paginationBuilder.Append("<div class=\"pagination\">Total <span style=\"color:red\" >" + totalItems + "</span> data.");
        //上一頁HTML,目前頁面大於1則啟用連結,否則就禁用
        paginationBuilder.Append(page > 1 ? string.Format("<a href=\"{0}page={1}\"> <<< </a>", targetPage, prev) : "<span class=\"disabled\"> <<< </span>");
        //頁碼選項 HTML 邏輯判斷
        //總頁數 不多於 (邏輯判斷共用參數+(3=代表當前頁+首或末兩頁),就不隱藏頁碼
        if (lastpage <= commonParameter+3) {
            for (int counter = 1; counter <= lastpage; counter++) {
                //counter等於當前頁則不加入連結,否則就加入連結
                paginationBuilder.Append(counter == page ? string.Format("<span class=\"current\">{0}</span>", counter) : string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, counter));
            }
        }
        //執行隱藏頁碼
        else {
            //只隱藏右側頁碼
            if (page < commonParameter) {
                for (int counter = 1; counter <= commonParameter; counter++) {
                    paginationBuilder.Append(counter == page ? string.Format("<span class=\"current\">{0}</span>", counter) : string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, counter));
                }
                //之後的頁碼用...省略
                paginationBuilder.Append("...");
                //加入倒數第2頁
                paginationBuilder.Append(string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, secondLast));
                //加入最末頁
                paginationBuilder.Append(string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, lastpage));
            }
            //中間頁碼,隱藏兩側頁碼
            else if (page >= commonParameter && page <= lastpage-commonParameter) {
                //加入第一頁+第二頁及...省略頁碼
                paginationBuilder.Append(string.Format("<a href=\"{0}page=1\">1</a>", targetPage));
                paginationBuilder.Append(string.Format("<a href=\"{0}page=2\">2</a>", targetPage));
                paginationBuilder.Append("...");
                for (int counter = page-adjacents; counter <= page+adjacents; counter++) {
                    //從當前頁的左側鄰近頁到右側鄰近頁正常添加頁碼 (當前頁不加連結)
                    paginationBuilder.Append(counter == page ? string.Format("<span class=\"current\">{0}</span>", counter) : string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, counter));
                }
                //之後的頁碼用...省略,加入倒數第二頁及最末頁
                paginationBuilder.Append("...");
                paginationBuilder.Append(string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, secondLast));
                paginationBuilder.Append(string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, lastpage));
            }
            ////只隱藏左側頁碼
            else {
                //加入第一頁+第二頁及...省略頁碼
                paginationBuilder.Append(string.Format("<a href=\"{0}page=1\">1</a>", targetPage));
                paginationBuilder.Append(string.Format("<a href=\"{0}page=2\">2</a>", targetPage));
                paginationBuilder.Append("...");
                for (int counter = lastpage-commonParameter; counter <= lastpage; counter++) {
                    paginationBuilder.Append(counter == page ? string.Format("<span class=\"current\">{0}</span>", counter) : string.Format("<a href=\"{0}page={1}\">{1}</a>", targetPage, counter));
                }
            }
        }
        //下一頁的 HTML 內容,目前頁面小於最末頁則啟用連結,否則就禁用
        paginationBuilder.Append(page < lastpage ? string.Format("<a href=\"{0}page={1}\">>>></a>", targetPage, nextPage) : "<span class=\"disabled\"> >>> </span>");
        paginationBuilder.Append("</div>\r\n");
    }
    return paginationBuilder.ToString();
}
#endregion
  • 🌵 於方法上方輸入/// 可自動快速生成註釋行,可對方法內容加入說明 (中文區域)。

  • 👀 自製分頁控制項完整程式碼參考 : 專案 GitHub 連結


7. 使用時可以從方案總管直接拖拉 .ascx 檔案進 .aspx 頁面就會自動生成分頁控制項如下

<uc1:WebUserControl_Page runat="server" ID="WebUserControl_Page" />


8. 於 .aspx 頁面的分頁控制項上方拉一個 asp:Literal 控制項用來送資料測試分頁功能。


9. 於 .aspx 頁面的 <head> 標籤內引入 pagination.css 分頁樣式,CSS 內容參考如下

div#pagination {
    height: 50px;
    margin-top: 3px;
}
    div#pagination .count {
        float: left;
        padding: 5px;
    }
    div#pagination .pages {
        float: right;
        padding: 5px;
    }
div#paginationTop .count {
    float: left;
    padding: 5px;
}
div#paginationTop .pages {
    float: right;
    padding: 5px;
}
div.pagination {
    padding: 0px;
    margin: 0px;
}
    div.pagination a {
        padding: 2px 5px 2px 5px;
        margin: 2px;
        border: 1px solid #8dab68;
        text-decoration: none; /* no underline */
        color: #5f7f39;
    }
        div.pagination a:hover, div.pagination a:active {
            border: 1px solid #5f7f39;
            color: #000;
        }
    div.pagination span.current {
        padding: 2px 5px 2px 5px;
        margin: 2px;
        border: 1px solid #5f7f39;
        font-weight: bold;
        background-color: #5f7f39;
        color: #FFF;
    }
    div.pagination span.disabled {
        padding: 2px 5px 2px 5px;
        margin: 2px;
        border: 1px solid #EEE;
        color: #DDD;
    }
.bold14 {
    font-family: Verdana, Arial, Helvetica, sans-serif;
    font-size: 14px;
    font-weight: bold;
}
.rederror {
    color: red;
}


10. 於 .aspx.cs 頁面加入以下資料邏輯程式碼及分頁控制項參數

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack) {
        loadList();
    }
}

private void loadList()
{
    //預設為第1頁
    int page = 1;
    //判斷網址後有無參數
    //也可用String.IsNullOrWhiteSpace
    if (!String.IsNullOrEmpty(Request.QueryString["page"])) {
        page = Convert.ToInt32(Request.QueryString["page"]);
    }

    //設定頁面參數屬性
    //設定控制項參數: 一頁幾筆資料
    WebUserControl_Page.limit = 5;
    //設定控制項參數: 作用頁面完整網頁名稱
    WebUserControl_Page.targetPage = "WebForm1.aspx";

    //建立計算分頁資料顯示邏輯 (每一頁是從第幾筆開始到第幾筆結束)
    //計算每個分頁的第幾筆到第幾筆
    var floor = (page - 1) * WebUserControl_Page.limit + 1; //每頁的第一筆
    var ceiling = page * WebUserControl_Page.limit; //每頁的最末筆

    //將取得的資料數設定給參數 count
    int count = 36; //總資料數,可修改數字測試分頁功能是否正常

    //設定控制項參數: 總共幾筆資料
    WebUserControl_Page.totalItems = count;

    //渲染分頁控制項
    WebUserControl_Page.showPageControls();

    //設定模擬資料內容
    StringBuilder listHtml = new StringBuilder();
    for (int i = floor; i <= ceiling; i++) {
        if (i <= count) {
            listHtml.Append($"<a href=''> --------- 第 {i} 筆資料 --------- </a></li><br /><br />");
        }
    }
    LiteralTest.Text = listHtml.ToString();
}


11. 模擬頁面反覆修改 count 總資料數,點擊分頁按鈕測試,完成自訂分頁控制項 ~



本日總結 :

📢 今天製作的分頁控制項邏輯,因為還有隱藏過多頁面的功能,原本參考的程式碼有奇怪的數字計算在控制,但因為沒有解釋數字計算的意義,所以看不懂原理,反覆修改數字測試後,將奇怪的數字計算整理,並拉出邏輯判斷共用參數將計算過程合理化為自己能理解的模式,由於過程較複雜,在程式碼內放了大量的註解解釋,希望大家可以理解。

  • 明日將介紹製作 NEWS List 頁面後端的相關細節。

上一篇
Day 17- 依 NEWS 前台頁面分析拆解後,逐步建立後台功能 (下) - 畫面內容互動 - ASP.NET Web Forms C#
下一篇
Day 19 - 將 NEWS 後台儲存資料提取後,送至前台渲染畫面 (上) - News List Page CTE 暫存表應用 - ASP.NET Web Forms C#
系列文
ASP.NET Web Forms 入門 - 30天建立遊艇網頁專案後端及後台功能 C#30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言