2015년 7월 29일 수요일

C# WebForm : 사용자 관리 (4) : 사용자 목록 페이지 - 페이징(Paging) 기능 만들기

이제 좀 귀찮지만 페이징 기능을 만들어 본다.

이건 사용자 컨트롤로 만들건데, 궂이 사용자 컨트롤로 안만들어도 되지만, 페이징이란게 워낙에 광범위하게 많이 쓰이다 보니, 사용자 컨트롤로 한번 만들어 놓으면 여러모로 유용하게 쓰인다.


일단 "UserControls" 폴더를 하나 만든 후, "ucListPager" 라는 유저 컨트롤을 추가.
폴더는 만들던 말던, 알아서 ... 관리만 알아서 잘할 수 있다면 된다.

만든 유저 콘트롤의 Html 을 보면 "Inherits" 라는 항목이 있는데, 이게 기본적으로 현재 파일을 생성한 폴더의 경로로 되어 있다.
보통은 궂이 바꿀 필요가 없지만, 앞으로 다른 소스에도 갖다 쓸려고 한다면, 좀 정리해 주는게 좋다. (물론 안해도 상관은 없다.)


저 명칭은 비하인드 코드의 class 명이므로 aspx 파일과 cs 파일의 명칭을 같이 맞춰 주면 된다. (기본 명칭에 폴더명이 들어가 있는 것은 저게 다른 소스와 명칭이 중복되면 안되기 때문.)


나의 경우 간단히 "ucListPager" 라고 명명하고(당연히 cs 파일의 class 명도 같이 바꿔줌) 간단하게 html 을 구성한다.

이걸 "MemberList.aspx" 에서 쓸려면....


쓸려는 aspx 파일의 상단에 사용자 컨트롤을 등록해 주고...


적당한 위치에 등록한 태그명을 이용해 넣어 주면 된다.
이렇게 하면...


요렇게 나타나는데, 아직은 데이터가 없어서 버튼만 나타나 있다.

이제 할 것은 기능 추가... 를 해야 하니, "ucListPager.cs" 파일을 아래와 같이 편집.

[ucListPager.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Text;

public partial class ucListPager : System.Web.UI.UserControl
{
    #region Control Property 설정

    // 전체 항목 수
    public int TotalRecordCount
    {
        get
        {
            int returnValue = 0;
            try
            {
                if (this.ViewState["TotalRecordCount"] != null)
                { returnValue = Convert.ToInt32(this.ViewState["TotalRecordCount"]); }
            }
            catch
            { returnValue = 0; }

            return returnValue;
        }
        set
        { this.ViewState["TotalRecordCount"] = value.ToString(); }
    }

    // 페이지 사이즈
    public int PageSize
    {
        get
        {
            int returnValue = 10;
            try
            {
                if (this.ViewState["PageSize"] != null)
                { returnValue = Convert.ToInt32(this.ViewState["PageSize"]); }
            }
            catch
            { returnValue = 10; }

            return returnValue;
        }
        set
        { this.ViewState["PageSize"] = value.ToString(); }
    }

    // 전체 페이지 수
    public int TotalPageCount
    {
        get
        {
            int totalPageCount = this.TotalRecordCount / this.PageSize + ((this.TotalRecordCount % this.PageSize) == 0 ? 0 : 1);
            return totalPageCount;
        }
    }

    // 현재 페이지
    public int CurrentPageIndex
    {
        get
        {
            int currentPageIndex = 1;
            try
            {
                if (this.ViewState["CurrentPageIndex"] != null)
                { currentPageIndex = Convert.ToInt32(this.ViewState["CurrentPageIndex"]); }
            }
            catch
            { currentPageIndex = 1; }

            if (currentPageIndex < 1)
            {
                currentPageIndex = 1;
                this.ViewState["CurrentPageIndex"] = currentPageIndex;
            }

            return currentPageIndex;
        }
        set
        { this.ViewState["CurrentPageIndex"] = value.ToString(); }
    }

    // 현재 페이지의 시작 아이템 넘버
    public int CurrentStartIndex
    {
        get
        {
            return (this.CurrentPageIndex - 1) * this.PageSize;
        }
    }

    // 페이지 인덱스의 최대 수
    public int MaxPageDisplaySize
    {
        get
        {
            int returnValue = 10;
            try
            {
                if (this.ViewState["MaxPageDisplaySize"] != null)
                { returnValue = Convert.ToInt32(this.ViewState["MaxPageDisplaySize"]); }
            }
            catch
            { returnValue = 10; }

            return returnValue;
        }
        set
        {
            this.ViewState["MaxPageDisplaySize"] = value.ToString();
        }
    }

    #endregion


    #region Control Override 설정

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        this.GeneratePageList();
    }

    #endregion



    protected void Page_Load(object sender, EventArgs e)
    {

    }


    #region 사용자 정의 메서드

    private void GeneratePageList()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("   ");

        try
        {
            int totalPageCount = this.TotalPageCount;
            int currentPageIndex = this.CurrentPageIndex;
            int maxPageList = this.MaxPageDisplaySize;

            if (0 == totalPageCount)
            {
                this.divPanel.Visible = false;
                return;
            }
            else
            {
                this.divPanel.Visible = true;
            }

            int startPageIndex = 0;
            if (0 == currentPageIndex % maxPageList)
            {
                startPageIndex = (currentPageIndex / maxPageList) * (maxPageList) - maxPageList + 1;
            }
            else
            {
                startPageIndex = (currentPageIndex / maxPageList) * maxPageList + 1;
            }

            int endPageIndex = 0;
            if (totalPageCount + 1 > (startPageIndex + maxPageList))
            {
                endPageIndex = startPageIndex + maxPageList;
            }
            else
            {
                endPageIndex = startPageIndex + (totalPageCount % maxPageList == 0 ? maxPageList : totalPageCount % maxPageList);
            }

            for (int i = startPageIndex; i < endPageIndex; i++)
            {
                if (i == CurrentPageIndex)
                {
                    sb.Append("" + i.ToString() + "");
                }
                else
                {
                    sb.Append("" + i.ToString() + "");
                }

                sb.Append("   ");
            }

            lblPageList.Text = sb.ToString();
        }
        catch
        {
        }
    }

    #endregion

}


--

요렇게 해놓으면 ... MemberList.aspx 파일에서 쓸수 있는데...


여기 "Property" 로 사용할 값들은....


사용자 컨트롤 태그에 직접 써넣어 지정 할 수 있다.

이런 식으로 소스 상에서 지정도 가능함.
암튼 이렇게 하고...


요렇게 테스트용 데이터를 DB에 넣어 놓고, 페이지를 로드 해보면....

요렇게 페이지 리스트가 나타난다.

지금은 1 ~ 10 페이지가 나타나는데, 이것은 사용자 컨트롤의 속성을 레코드 수 100개, 페이지 크기 10 으로 직접 지정 해놨기 때문에 10페이지로 나타난 것이다.

요걸 실제 데이터에 맞게 나오게 할려면...
MemberList.apsx 에서 데이터를 조회 하는 부분의 마지막에 전체 아이템 수를 지정하면 된다.


요렇게 리스트를 만드는 과정중에 전체 아이템 수를 지정하면 된다.
주의 할점은 바인딩한 값이, Tables[0] 가 아니라 Tables[1] 이다.

요건 이전에 만들었던 DB 쿼리를 보면...

이렇게 실제로는 저장 프로시져안에 2개의 쿼리가 실행되고 있다.
이게, 처음 쿼리는 이번 페이지의 아이템 목록이며, 아래 쿼리는 지정된 검색 조건하의 전체 아이템 수.

그렇기 때문에, 받아온 데이터셋을 디버그 모드에서 보면...


이렇게 조회된 테이블이 2개가 있음을 알 수 있다.
이중에서 2번째 테이블인 전체 아이템 수 값을 페이져에 지정하는 것이다.

이렇게 하고, 페이지를 다시 로드 해보면...


이렇게 실제 값으로 페이지 목록을 만들어 준다.

여기까지는 됐고, 이제 실제 클릭시 이동 기능을 만들어 준다.
사용자 컨트롤에서 클릭한 페이지를 사용자 컨트롤을 사용하고 있는 페이지로 전달 하기 위해서는 이벤트를 이용한다.


이전에 페이지 목록을 만드는 로직에 보면 각 페이지 번호에 "goSelectPage()" 라는 자바 스크립트를 링크 시키고 있는게 보일 것이다.

이제 이 자바스크립트를 생성해 준다.


스크립트는 간단하게 숨겨진 텍스트필드에 클릭한 값을 지정하고, 숨겨놓은 페이지 이동 버튼을 클릭하게 하는 자바스크립트.

그러면, 버튼 클릭시 이벤트 프로세스를 작성해 주자.

버튼을 클릭하면, "PageIndexChange" 이벤트를 발생하게 한다.
이렇게  "PageIndexChange" 이벤트를 만들어 놓았으니...


이제 "MemberList.aspx" 의 Html 에서 페이징 사용자 컨트롤에  "onPageIndexChange" 이벤트를 등록 할 수 있다.

사용자 컨트롤 태그에 "onPageIndexChange" 이벤트 발생시 실행할 프로세스로 "pagerPagingIndexChang" 를 지정해 놓고...

이렇게 지정된 페이지 번호로 목록을 다시 불러 오게 한다.
그러면...

이렇게 페이지 번호를 클릭할 때 마다, 목록을 새로 불러 오게 된다.
다음으로 다음 페이지, 이전 페이지 ... 등등을 구현할려면...


각 버튼에 onClick 이벤트를 등록해 주고...


beHind Code 에 처리 로직을 등록.
간단히, 어디를 조회할지 페이지번호를 계산해 놓고, 페이지 재로드 이벤트를 발생시키면 된다.

이렇게 하면 기능은 모두 끝....

마지막으로...


페이져용 스타일시트를 만들어서 ....


사용자 컨트롤을 사용하고 있는 페이지에 넣어주면...
(물론 사용자 컨트롤 자체에 넣어 줘도 되지만...)


이렇게 사용자 리스트 완성.

[최종 ucListPager.ascx]

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ucListPager.ascx.cs" Inherits="ucListPager" %>

<div id="divPanel" class="pageNavigation" runat="server">
    <div class="ucPager_div">
        <table class="ucPager_table">
        <tr>
            <td>
                <asp:Button id="btnGoFirstPage" CssClass="btnGoFirstPage" runat="server" Text="<<" OnClick="btnGoFirstPage_Click" />
            </td>
            <td>
                <asp:Button id="btnGoPreviousPage" CssClass="btnGoPreviousPage" runat="server" Text="<" OnClick="btnGoPreviousPage_Click" />
            </td>
            <td>
               <asp:Label id="lblPageList" CssClass="lblPageList" runat="server" Text=""  />
            </td>
            <td>
                <asp:Button id="btnGoNextPage" CssClass="btnGoNextPage" runat="server" Text=">" OnClick="btnGoNextPage_Click" />
            </td>
            <td>
                <asp:Button id="btnGoLastPage" CssClass="btnGoLastPage" runat="server" Text=">>" OnClick="btnGoLastPage_Click" />
            </td>
        </tr>
        </table>
    </div>

    <asp:Button ID="btnGoSelectedPage" runat="server"  style="display: none;" OnClick="btnGoSelectedPage_Click" />
    <asp:TextBox ID="hdnCurrentPageIndex" runat="server" style="display: none;" />
</div>


<script type="text/javascript">
function goSelectedPage(pageIndex)
{       
    var btnGoSelectedPage = $get("<%=btnGoSelectedPage.ClientID %>");
    var hdnCurrentPageIndex = $get("<%=hdnCurrentPageIndex.ClientID %>");

    hdnCurrentPageIndex.value = pageIndex;
    btnGoSelectedPage.click();
}
</script>



[최종 ucListPager.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Text;

public partial class ucListPager : System.Web.UI.UserControl
{
    #region Control Property 설정

    // 전체 항목 수
    public int TotalRecordCount
    {
        get
        {
            int returnValue = 0;
            try
            {
                if (this.ViewState["TotalRecordCount"] != null)
                { returnValue = Convert.ToInt32(this.ViewState["TotalRecordCount"]); }
            }
            catch
            { returnValue = 0; }

            return returnValue;
        }
        set
        { this.ViewState["TotalRecordCount"] = value.ToString(); }
    }

    // 페이지 사이즈
    public int PageSize
    {
        get
        {
            int returnValue = 10;
            try
            {
                if (this.ViewState["PageSize"] != null)
                { returnValue = Convert.ToInt32(this.ViewState["PageSize"]); }
            }
            catch
            { returnValue = 10; }

            return returnValue;
        }
        set
        { this.ViewState["PageSize"] = value.ToString(); }
    }

    // 전체 페이지 수
    public int TotalPageCount
    {
        get
        {
            int totalPageCount = this.TotalRecordCount / this.PageSize + ((this.TotalRecordCount % this.PageSize) == 0 ? 0 : 1);
            return totalPageCount;
        }
    }

    // 현재 페이지
    public int CurrentPageIndex
    {
        get
        {
            int currentPageIndex = 1;
            try
            {
                if (this.ViewState["CurrentPageIndex"] != null)
                { currentPageIndex = Convert.ToInt32(this.ViewState["CurrentPageIndex"]); }
            }
            catch
            { currentPageIndex = 1; }

            if (currentPageIndex < 1)
            {
                currentPageIndex = 1;
                this.ViewState["CurrentPageIndex"] = currentPageIndex.ToString();
            }
            return currentPageIndex;
        }
        set
        {
            this.ViewState["CurrentPageIndex"] = value.ToString();
        }
    }

    // 현재 페이지의 시작 아이템 넘버
    public int CurrentStartIndex
    {
        get
        {
            return (this.CurrentPageIndex - 1) * this.PageSize;
        }
    }

    // 페이지 인덱스의 최대 수
    public int MaxPageDisplaySize
    {
        get
        {
            int returnValue = 10;
            try
            {
                if (this.ViewState["MaxPageDisplaySize"] != null)
                { returnValue = Convert.ToInt32(this.ViewState["MaxPageDisplaySize"]); }
            }
            catch
            { returnValue = 10; }

            return returnValue;
        }
        set
        {
            this.ViewState["MaxPageDisplaySize"] = value.ToString();
        }
    }

    #endregion


    #region Control Override 설정

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        this.GeneratePageList();
    }

    #endregion



    protected void Page_Load(object sender, EventArgs e)
    {

    }


    #region 사용자 정의 메서드

    private void GeneratePageList()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("&nbsp; &nbsp;");

        try
        {
            int totalPageCount = this.TotalPageCount;
            int currentPageIndex = this.CurrentPageIndex;
            int maxPageList = this.MaxPageDisplaySize;

            if (0 == totalPageCount)
            {
                this.divPanel.Visible = false;
                return;
            }
            else
            {
                this.divPanel.Visible = true;
            }

            int startPageIndex = 0;
            if (0 == currentPageIndex % maxPageList)
            {
                startPageIndex = (currentPageIndex / maxPageList) * (maxPageList) - maxPageList + 1;
            }
            else
            {
                startPageIndex = (currentPageIndex / maxPageList) * maxPageList + 1;
            }

            int endPageIndex = 0;
            if (totalPageCount + 1 > (startPageIndex + maxPageList))
            {
                endPageIndex = startPageIndex + maxPageList;
            }
            else
            {
                endPageIndex = startPageIndex + (totalPageCount % maxPageList == 0 ? maxPageList : totalPageCount % maxPageList);
            }

            for (int i = startPageIndex; i < endPageIndex; i++)
            {
                if (i == CurrentPageIndex)
                {
                    sb.Append("<b>" + i.ToString() + "</b>");
                }
                else
                {
                    sb.Append("<a href=\"javascript:goSelectedPage('" + i.ToString() + "')\">" + i.ToString() + "</a>");
                }

                sb.Append("&nbsp; &nbsp;");
            }

            lblPageList.Text = sb.ToString();
        }
        catch
        {
        }
    }

    public delegate void PagerClickEventHandler (object sender);
    public event PagerClickEventHandler PageIndexChange;
    private void CallPageIndexChangeEvent()
    {
        this.PageIndexChange(this);
    }

    protected void btnGoFirstPage_Click(object sender, EventArgs e)
    {
        this.CurrentPageIndex = 1;
        CallPageIndexChangeEvent();
    }

    protected void btnGoPreviousPage_Click(object sender, EventArgs e)
    {
        if (2 <= this.CurrentPageIndex)
        {
            this.CurrentPageIndex = this.CurrentPageIndex - 1;
            CallPageIndexChangeEvent();
        }
    }

    protected void btnGoNextPage_Click(object sender, EventArgs e)
    {
        if (this.TotalPageCount > this.CurrentPageIndex)
        {
            this.CurrentPageIndex = this.CurrentPageIndex + 1;
            CallPageIndexChangeEvent();
        }
    }

    protected void btnGoLastPage_Click(object sender, EventArgs e)
    {
        this.CurrentPageIndex = this.TotalPageCount;
        CallPageIndexChangeEvent();
    }

    protected void btnGoSelectedPage_Click(object sender, EventArgs e)
    {
        try
        {
            int newPageIndex = Convert.ToInt32(hdnCurrentPageIndex.Text);
            this.CurrentPageIndex = newPageIndex;
            this.CallPageIndexChangeEvent();
        }
        catch
        { }
    }

    #endregion

}




흐... 힘들다...