Link to home
Start Free TrialLog in
Avatar of CVSmarc
CVSmarc

asked on

Custom Paging datagrid

Hi all
I know that i am asking for this in .net 1.1 and datagrids, but unfortunately i have to work with a legacy system here instead of 2.0.

Now what i have created a CustomDataGrid class which Inherits System.Web.UI.Page. Within this class i am wanting it to deal with two things one bi-directional sorting and custom paging. The idea behind creating this class is that i can use my new CustomDataGrid and it will automatically have the given requirement and i don’t have to go around and include half a dozen additional controls to provide the additional functionality.

I have got the sorting working alright, but i am having a bit of trouble with the paging. I have spent a lot of time looking at different tutorials and stuff on the web but they all seem to miss the big picture.
    - The pager needs to be in the following format (were 6 is the current page)    
       First Prev 4 5 6 7 8 Next Last of 45
    - ALL of this items in the pager to link thought to the appropriate page (not  
      just the  First/Prev/Next/Last links)
    - I need this pager to be in the footer of the datagrid (again looking around
      on the web, i couldn't find a datagrid that has a custom pager in the footer
      of the datagrid)
    - If the pager can be put into the footer i need it on the right and 'Record x -
      y of z'  on the right and the pager on the left (note i already can get all
      of the information about what page i am on and how many records
      there are in total and can derive the rest)

Now i tried to have a go at this but kept running into a few brick walls (i might be wrong though, i'm still working my way through .net so please help me out if i making a big mistake)
     - because these pager items need to run events when they are clicked
       i can't just build up the formatted HTML
     - because i don’t want to include any additional controls on the each page
       that i use this new class in, i have to dynamically build up the controls
       and place them
     - because i am dynamically building up the controls, i need to dynamically
       bind events to these controls
     - because they are dynamically added controls it makes it harder to add the
       html formatting that i need to the pager (i.e. '<div class="navPagging">
       <img src="image/buttonfirst.jpg" border="0"><img src="image/buttonprev.jpg"
        border="0"><div>&nbsp; 3 4 <span class="selected">5</span> 6 7 &nbsp;
       </div><img src="image/buttonnext.jpg"><img src="image/buttonlast.jpg">
       <div>&nbsp; of 34</div></div>', just pretend that it has the href tags were
       they would need to go)
     - because of the nested design of the pager and because of the dynamic creation
       of each of the controls i would need to a few nested placeholders and the controls
       being added at the relevant places

Thus, when looking at this method and the problems I was running into I thought that there must be an easer way of doing what i need to do. So i decided to ask some people smarter than me.
Thanks ant
Avatar of asdavey
asdavey

Hmmm.

I make this comment fully aware of not knowing exactly what situations you've tried and what design tradeoff decisions you've already made.

I would strongly encourage you to try and implement your solution using a UserControl. Tonight I'm actually working on a very similar control (but in .Net 2 which has the GridView which is very easy to work with). By using a UserControl, you can create a composite control. Basically you'd be hooking up your custom rendered pager with .Net 1.1's DataGrid control.

In the UserControl (let's call it DataGridEx) the markup could look something like:

<div>
  <asp:datagrid runat="server" id="datagrid" autogeneratecolumns="true"/>
  <asp:placeholder runat="server" id="pager"/>
</div>

In your DataGridEx's PreRender method, you could determine what links to pages you'd like to display (eg First, Prev, 2, 3, 4 .... etc) and add them as LinkButton's to the placeholder control 'pager'. Hook up the events and you are away.

On the page that you would like to have your DataGridEx appear, the page would look like:

<%@ Register TagPrefix="MyUserControls" TagName="DataGridEx" src="~/usercontrols/datagridex.ascx" %>

<myusercontrols:datagridex runat="server"/>

If you've not had any previous experience with UserControls I'd suggest tackling something a little simpler for your first attemp (a login page or something).

If you've already looked down this path and have made the decision that a Server control is the way that you have to go, I'd suggest inhieriting from WebControl instead of Page since the Page control is very much targetted to rendering pages and not controls.

HIH

Andy

Avatar of CVSmarc

ASKER

Thanks a lot for your reply.

Ok the biggest problem i have with what you are suggesting is the "In your DataGridEx's PreRender method, you could determine what links to pages you'd like to display (eg First, Prev, 2, 3, 4 .... etc) and add them as LinkButton's to the placeholder control 'pager'. Hook up the events and you are away.".

As per your suggestion i have put together the below sub which should be able to handles the construction of the paging.
 
        Protected Sub CreatePaging(ByVal objPlaceHolder As PlaceHolder)
            Dim PagerFirstImg As New HyperLink
            Dim PagerLastImg As New HyperLink
            Dim PagerPreviousImg As New HyperLink
            Dim PagerNextImg As New HyperLink

            Dim PagerNumberPosA As New HyperLink
            Dim PagerNumberPosB As New HyperLink
            Dim PagerNumberPosC As New HyperLink
            Dim PagerNumberPosD As New HyperLink
            Dim PagerNumberPosE As New HyperLink

            If _intPageCurrentPage > 0 Then
                'Adds the first image
                PagerFirstImg.ImageUrl = objPlaceHolder.ResolveUrl("~/image/buttonfirst.jpg")
               .....  'Event code needs to be added in here
                'Adds the prvious image
                PagerPreviousImg.ImageUrl = objPlaceHolder.ResolveUrl("~/image/buttonprev.jpg")
               .....  'Event code needs to be added in here
            End If
 
            If _intPageRecordEnd < _intPageTotalRecordCount Then
                'Adds the first image
                PagerNextImg.ImageUrl = objPlaceHolder.ResolveUrl("~/image/buttonnext.jpg")
               .....  'Event code needs to be added in here
                'Adds the prvious image
                PagerLastImg.ImageUrl = objPlaceHolder.ResolveUrl("~/image/buttonlast.jpg")
               .....  'Event code needs to be added in here
            End If

           .......................      'Code to constuct the PagerNumberPosX controls

            'Sets it to the place holder
            .......................    'Add in the additional HTML and Bind all the controls the the placeholder
        End Sub


Now i am pretty sure that i can hook up the events and can write the code to construct the PagerNumberPosX control, the only problem i have at the moment is how to add in my formatting HTML. The HTML that is constructed for each of the controls should be something like the following:
       PagerFirstImg - <a href="javascript:...."><img src="image/buttonfirst.jpg" border="0"></a>
       PagerPreviousImg - <a href="javascript:...."><img src="image/buttonprev.jpg" border="0"></a>
       PagerNumberPosA - <a href="javascript:....">3</a>
       PagerNumberPosB - <a href="javascript:....">4</a>
       PagerNumberPosC - <a href="javascript:....">5</a>
       PagerNumberPosD - <a hef="javascript:.....">6</a>
       PagerNumberPosE - <a href="javascript:....">7</a>
       PagerNextImg - <a href="javascript:...."><img src="image/buttonnext.jpg"></a>
       PagerLastImg - <a href="javascript:...."><img src="image/buttonlast.jpg"></a>

I need to turn it into to something like this (with the added href offcourse):
       <div class="navPagging"><img src="image/buttonfirst.jpg" border="0">
       <img src="image/buttonprev.jpg" border="0"><div>&nbsp; 3 4
       <span class="selected">5</span> 6 7 &nbsp;</div><img src="image/buttonnext.jpg">
       <img src="image/buttonlast.jpg"><div>&nbsp; of 34</div></div>

Now if i understand the '.Controls.Add(....)' can't just plain HTML inserted into it. So i need a what of binding the objects all together, with the necessary HTML and i really dont want to go about it by creating a whole heap of nested PlaceHolder's.

Lastly i am just wondering why you would suggest creating a user control over a CustomDataGrid class? It's just that atm i am not bound to using a class, but i just thought that using a class would give me more functionality.

Thanks
ant
Ant,

With regards to formatting your pager, a lot of what you are trying to do can be best acheived through a combination of code behind and controls embeeded in the .ascx page (some people refer to this as the designer source).

Your DataGridEx.ascx could look something like this:

<div>
  <asp:datagrid runat="server" id="datagrid" allowpaging="true" pagerstyle-visibile="false"/>
  <div class="navpaging">
    <asp:imagebutton runat="server" id="first" imageurl="~/image/buttonfirst.jpg" oncommand="PageNavigation" commandaname="First"/>
    <asp:imagebutton runat="server"  id="prev" imageurl="~/image/buttonprev.jpg" oncommand="PageNavigation" commandname="Prev"/>
   
    <asp:placeholder runat="server" id="pageNumbersPlaceHolder"/>

    <asp:imagebutton runat="server" id="next" imageurl="~/image/buttonnext.jpg" oncommand="PageNavigation" commandaname="Next"/>
    <asp:imagebutton runat="server"  id="last" imageurl="~/image/buttonlast.jpg" oncommand="PageNavigation" commandname="Last"/>

  </div>
</div>

The above has taken care of rendering our image buttons and has given us a nice easy way to add the numbers that link to the various pages. So, in Page_PreRender you could have some code that looks like this (you will have to forgive my vb.net ... its a little rusty):

Page_PreRender(sender as Object, e as EventArgs)

  Dim totalPageCount as Integer = datagrid.pagecount
  Dim currentPage as Integer = datagrid.currentpageindex + 1
 
  '
  ' Assuming that we want to display the page numbers of say 2 either side of our current page number
  '

  If (currentPage <= 2) Then
    ' Exercise for ant to complete
  Else if (currentPage >= totalPageCount - 2) Then
    ' Execise for ant to complete
  Else

    Dim pageNumbers as New LiteralControl()

    pageNumbers.Text = string.Format("&nbsp;{0}&nbsp;{1}&nbsp;<span style='font-weight:bold;'>{2}</span>&nbsp;{3}&nbsp;{4}", currentPage - 2, currentPage - 1, currentPage, currentPage + 1, currentPage + 2)

    pageNumbersPlaceHolder.Controls.Add(pageNumbers)

  End If

End Sub

And if you are wondering about the OnCommand event handler for the image buttons it could look something like this:

      Protected Sub PageNavigation(ByVal sender As Object, ByVal e As CommandEventArgs)

            Select Case (e.CommandName)
                  Case "First"
                        datagrid.CurrentPageIndex = 0

                  Case "Prev"
                        datagrid.CurrentPageIndex = datagrid.CurrentPageIndex - 1

                  Case "Next"
                        datagrid.CurrentPageIndex = datagrid.CurrentPageIndex + 1

                  Case "Last"
                        datagrid.CurrentPageIndex = datagrid.PageCount - 1

            End Select

      End Sub

You asked why I suggest the user control over writing your own. I find that user controls are fantastic for writing composite controls (controls that combine the behaviour of one or more controls into a single package). What makes them so good is the ability to leverage the power of existing controls and customise their behaviour quickly. You are correct in saying that a class will give you more functionality, and sometimes that is the only answer. But for this particular problem, a UserControl can be implemented 10 times faster than implementing your own control from scratch.

HIH

Andy
Avatar of CVSmarc

ASKER

Thanks allot for your comments.
This is starting to make a bit more sense now. Just one other thing... If i wanted to make it possible for the user to select the one of the two numbers on either side of the current page and actually go to that page, how would i go about going that.
ant
Avatar of CVSmarc

ASKER

Also would it be possible to have one placeholder and have all the imagebutton controls and the page numbers put into that one placeholder or does that bring be back to the same place i was to start (having to create all the controls by hand, binding the events and then adding them all together)??
Avatar of Anthony Perkins
It is a violation of the Membership Agreement to have more than 500 points per question.  Please delete the folllowing question, before someone contributes:
https://www.experts-exchange.com/questions/21815455/Custom-Paging-pointer.html

Alternatively, post a message in Community Support to have it converted to a "pointer" question for 20 points.

Also, the following questions are considered abandoned, please attend to them:
1 03/16/2006 500 HTML - PDF Generation  Open Active Server Pages (ASP)
2 01/31/2006 500 Detect difference between single click a...  Open JavaScript
To make it possible to select a link on both sides of the displayed page index you could use two HyperLink controls on either side. Set thier CommandName to "Go", their CommandArgument to the page number you want to navigate to and their OnCommand event handler to PageNavigation.

Then in the PageNaviagition add a new Case like so:

  Case "Go":
    datagrid.CurrentPageIndex = Integer.Parse(e.CommandArgs)

As for your comment regarding hosting all of the navigation controls into a single PlaceHolder, yeah that will mean that you will have to go back to rendering all of that HTML by hand. However, maybe what you want to do is host them all in an asp:Panel control.
Before when I said HyperLink control I meant LinkButton. Any control can be used, but it would be better to use one that gives you the look you want and supports the OnCommand event.
Avatar of CVSmarc

ASKER

Thanks for the reply.

One thing i am trying to do is something along this line...
                For i = intPagerStartPos To intPagerEndPos
                    strPagerHTML = strPagerHTML & "<a href=""javascript:ChangePage(" & i.ToString() & ")"">" & i & "</a> "
                Next

and then have this

            function ChangePage(intPage) {
                //Save the page number clicked to the hidden field
                document.getElementById("PageNumber").value = intPage;
                //Calls the __doPostBack function to post back the form and execute the PageClick event
                __doPostBack('PageIndexChanged','');
            }

But i seem to be having some trouble with .net detecting that it needs to run the PageIndexChanged event/function. Got any ideas?

If i can get this to work, is it a reasonable way of going about this or not real? I am keen to come up with a solution that require the use of only one placeholder and all the different buttons/items/links be placed inside it. That way all i need to go is have the one control on each page and if i decide to change the pager it should make things easer. The only thing i am worried about is that if i go with this approach i have to have a method that creates the additional webcontrols for each item, add the handers for each of those items and then bind them all together and include the formatting HTML. From what i have seen there doesn't seem to be a 'functional' way of doing this (by functional i don’t mean using functions i just mean the overall process is not functional).

Thanks
ant
To be honest I've never handcoded javascript calling back to the server side like that before so I'm little help to you on that side of things, except to say that I would be surprised if using a smattering of server side controls would be more difficult to implement and maintain than using the client rendered javascript approach. The beauty of the server side controls approach is it just works.
Avatar of CVSmarc

ASKER

Ok. If you believe that going down server side controls track is the better way to go could you please explain to be how i can go about doing it.

As i have said in the above, the reason why i am hesitant to go down this track in because i want what ever solution i have to only use 1 placeholder and as such unless i am mistaken this means that i have to dynamically create and add the other 10 (note this could change depending on how many pages at once a developer wishes to use and if i have to use any more for formatting) or so server side controls. If i am correct here it then means i have to dynamically add events to all of these as well. Also no one has explained to me yet how i can do all of that and include the additional HTML formatting i need.

If you could answer me this i would be most appreciative. As you can see, if this is the approach i need/should to go down (because i am still learning) why i am hesitant to do it and think that there must be an easer way, whilst still keeping the 1 placeholder (or if you know a way to append to controls to the bottom of the datagrid event be, that way i wouldn't need even a placeholder). My am is for me to be able to use this 'customDataGrid' that i have created and automatically include all the additional function and features that i need.

Thanks
ant
If you want the developer to be able to specify how many page numbers are displayed in between the prev and next commands then that changes things a little.

The reason being is we don't want to be generating controls on the fly if we wan't to be able to handle their events. It can be done, but its what I would consider an advanced technique (you need to fully understand how .Net "magically" wires up javascript events with server side events, and then make sure that all controls that could potentially be the target of an event be created before the LoadViewState method completes).

So you have two options. Use the javascript method that you were talking about earlier (which may work...I just haven't seen any code that does it like that)

OR you could put a theoretical limit on the number of page numbers that will be displayed as links. Say you choose 5. You then have in your ascx page the following:
  <div class="navpaging">
    <asp:imagebutton runat="server" id="first" imageurl="~/image/buttonfirst.jpg" oncommand="PageNavigation" commandaname="First"/>
    <asp:imagebutton runat="server"  id="prev" imageurl="~/image/buttonprev.jpg" oncommand="PageNavigation" commandname="Prev"/>
   
    <asp:linkbutton runat="server" id="navLink1" oncommand="PageNavigation" commandname="goto"/>
    <asp:linkbutton runat="server" id="navLink2" oncommand="PageNavigation" commandname="goto"/>
    <asp:linkbutton runat="server" id="navLink3" oncommand="PageNavigation" commandname="goto"/>
    <asp:linkbutton runat="server" id="navLink4" oncommand="PageNavigation" commandname="goto"/>
    <asp:linkbutton runat="server" id="navLink5" oncommand="PageNavigation" commandname="goto"/>

    <asp:imagebutton runat="server" id="next" imageurl="~/image/buttonnext.jpg" oncommand="PageNavigation" commandaname="Next"/>
    <asp:imagebutton runat="server"  id="last" imageurl="~/image/buttonlast.jpg" oncommand="PageNavigation" commandname="Last"/>
  </div>

And then in your prerender method do something like this

  if (datagrid.CurrentPageIndex > 2) Then
    navLink1.Text = (datagrid.currentPageIndex - 2).ToString()
    navLink1.CommandArgument = (datagrid.currentPageIndex - 2).ToString()
    navLink1.Visible = true
  else
    navLink1.Visible = false
  end if

  if (datagrid.CurrentPageIndex > 1) Then
    navLink2.Text = (datagrid.currentPageIndex - 1).ToString()
    navLink2.CommandArgument = (datagrid.currentPageIndex - 1).ToString()
    navLink2.Visible = true
  else
    navLink2.Visible = false
  end if

  navLink3.Text = (datagrid.currentPageIndex).ToString()
  navLink3.CommandArgument = (datagrid.currentPageIndex).ToString()

  ' now do the same for the page numbers on the other side of the current page index etc.


Obviously this solution doesn't scale that well if you want people to be able to click up to 100 page numbers.

HIH


Avatar of CVSmarc

ASKER

Thanks very much for your work.
The one thing i have found is this, http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcongeneratingclient-sidejavascriptforpostback.asp. It seems to be the proper way of implementing what i am thinking of doing with the JavaScript post back. I am just having a bit of trouble hooking it up with my code and detecting that the postback was an event that was triggered by the custom paging. If you can shed any light on this, that would be great.
Thanks ant  
OK so what you would now do is this:

In the PreRender method you would redner the following LiteralControl

Dim pages as new LiteralControl()

For i as Integer = -2 To 2
  Dim page as String = (datagrid.CurrentPageIndex + i).ToString()
  pages.Text += string.Format("<a href=""javascript:{0}"">{1}</a>&nbsp;", Page.GetPostBackEventReference(Me, page), page)
Next

pagesPlaceHolder.Controls.Add(pages)

Then you need to make sure you user control class implements the interface IPostBackEventHandler like so

      Public Sub RaisePostBackEvent(eventArgument As String) Implements IPostBackEventHandler.RaisePostBackEvent
         Dim index as Integer = Integer.Parse(eventArgument)
         DataGrid.CurrentPageIndex = index
      End Sub

HIH

Andy
Avatar of CVSmarc

ASKER

Just thought i would fill you in on how i got on. In short I have no luck in getting the above code up and running. I think this is mainly due to the way I have set up my customDataGrid class.

But basically to get this working, i created the HTML need to make up the pager as a string (thus cutting out all the complexity surrounded by dynamically creating all the controls i would need and binding events) and placed that string within a literal on the ASPX page. Then when ever the user selected one of the options within the custom build pager, a JavaScript function runs that received parameter (that indicated the page index that the user wishes to go to), placed that value within a hidden text box and posted the page back to the server. Now because this hidden is a webcontrol and has a server onchangeevent enabled on it, i am able to capitate any activity that the user makes with the paging as an event.

Now i don’t know how "elegant" you would consider this, but it works really well and i am able to create the most complex paging systems i want (as long as i can make the HTML for it) and the system will be able to use this custom "control" (although strictly specking I don’t think it is custom control in the strictest sense of the word) and run the required events.
Thanks again

ant
ASKER CERTIFIED SOLUTION
Avatar of GranMod
GranMod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial