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 页面後端的相关细节。

<<:  从零开始的8-bit迷宫探险【Level 24】谁才是高玩?纪录本机最高得分

>>:  Day19:别说那麽多废话,讲重点

Day2 - Canvas基础概论 I - 成为Canvas Ninja ~ 理解2D渲染的精髓

Let's Start From Scratch 本系列文章的头几篇我决定还是带点基础的东西,但是我...

Day18 Combine 05 - Operators 类型介绍 : 转换操作符

转换操作符 map/mapError map操作符会执行给定的闭包,将上游发布的内容进行转换,然後再...

SPML用於将跨资讯系统之创建和管理实体和属性的过程自动化

在一个电信行业的技术词汇,它是指为了向用户提供(新)服务的准备和安装一个网络的处理过程。它也包括改变...

认识与了解WebDAV

最後云端,为完赛画下彩虹~~ 为了使部署的云端硬碟能像Google Drive一样使用串流、WebU...

Day 10 - 来谈谈在P5最常用的色彩学

p5 中的 color 物件: 先认识两个色彩学 可以了解 https://www.instagra...