📜 ⬆️ ⬇️

Implementing a Reverse NumericPagerField in ASP.NET

The latest version of the .NET Framework - 3.5 has brought us many improvements and new features, one of which is the sweet couple of ListView and DataPager.

At one point, I wanted to implement using a DataPager navigation between pages, in which the numbers would go from larger to smaller, as it was done, for example, on Habré or Lepre. This order is most suitable for displaying a sorted list, for example, by date — first a new one. Normal counting is elementarily implemented using the NumericPagerField field, but this control does not contain methods for changing the counting direction.

Googling did not give a ready answer to the question asked, so I had a choice before me - either use TemplatePagerField and get completely confused or try to somehow change the ready NumericPagerField. I liked the second way more, I went on it.
')

At first I wanted to rewrite the CreateDataPager method of NumericPagerField, but it turned out that this was impossible, so I had to inherit the base class NumericPagerField, DataPagerField, and create my own pager. In this case, I used the source code. NET Framework 3.5, which are freely available.

So, create a reversible pager. To do this, create a new class ReversedNumericPagerField, inheriting DataPagerField:

public class ReversedNumericPagerField: DataPagerField
{
private int _startRowIndex;
private int _maximumRows;
private int _totalRowCount;

public ReversedNumericPagerField ()
{
}
}
* This source code was highlighted with Source Code Highlighter .


In order for our new pager to work somehow, it is necessary to implement the following methods in it: CreateField, CreateDataPagers and HandleEvent. We also need some global variables: _startRowIndex - the index of the list item that will be displayed first, _maximumRows - the number of list items that will be displayed on one page and _totalRowCount - the number of items in the list.

CreateField method.
With this, everything is simple - returns an instance of the ReversedNumericPagerField class.

protected override DataPagerField CreateField ()
{
return new ReversedNumericPagerField ();
}
* This source code was highlighted with Source Code Highlighter .


The CreateDataPagers method.
We need this method - it determines what and when to display on the page and creates the necessary controls.

public override void CreateDataPagers (DataPagerFieldItem container, int startRowIndex, int maximumRows, int totalRowCount, int fieldIndex)
{
_startRowIndex = startRowIndex;
_maximumRows = maximumRows;
_totalRowCount = totalRowCount;

CreateDataPagersForCommand (container, fieldIndex);
} * This source code was highlighted with Source Code Highlighter .


It is worth noting that the original NumericPagerField can generate both normal links to pages, using pages to pages, using QueryStrings, and buttons like LinkButton. I only needed the latter, but if you need both cases, then it is best to implement them in different functions and select the right one, depending on the property of the control (controller). The functionality for LinkButton in this case is implemented by the CreateDataPagersForCommand function, which by means of “simple” mathematical transformations determines which digit to draw on the button and creates this button.

private void CreateDataPagersForCommand (DataPagerFieldItem container, int fieldIndex)
{
int currentPageIndex = _startRowIndex / _maximumRows;
int firstButtonIndex = (_startRowIndex / (ButtonCount * _maximumRows)) * ButtonCount;
int lastButtonIndex = firstButtonIndex + ButtonCount - 1;
int lastRecordIndex = ((lastButtonIndex + 1) * _maximumRows) - 1;
int totalPages = ( int ) Math .Ceiling (( double ) _totalRowCount / _maximumRows);

for ( int i = 0; i <ButtonCount && _totalRowCount> ((i + firstButtonIndex) * _maximumRows); i ++)
{
if (i + firstButtonIndex == currentPageIndex)
{
Label pageNumber = new Label ();
pageNumber .Text = (totalPages - i - firstButtonIndex) .ToString ();
container.Controls.Add (pageNumber);
}
else
{
container.Controls.Add (CreateNumericButton ((totalPages - i - firstButtonIndex) .ToString (), fieldIndex.ToString (), (i + firstButtonIndex) .ToString ()));
}
container.Controls.Add ( new LiteralControl ( "" ));
}
}
* This source code was highlighted with Source Code Highlighter .


Here, to create the button, the following function is called, which, in fact, returns an instance of LinkButton with the specified properties.

private Control CreateNumericButton ( string buttonText, string commandArgument, string commandName)
{
LinkButton button = new LinkButton ();
button.Text = buttonText;
button.CommandName = commandName;
button.CommandArgument = commandArgument;
return button as Control;
}
* This source code was highlighted with Source Code Highlighter .


HandleEvent method.
This method handles events in our pager and was almost unchanged from the inherited class.

public override void HandleEvent (CommandEventArgs e)
{
int newStartRowIndex = -1;
int currentPageIndex = _startRowIndex / DataPager.PageSize;
int firstButtonIndex = (_startRowIndex / (ButtonCount * DataPager.PageSize)) * ButtonCount;
int lastButtonIndex = firstButtonIndex + ButtonCount - 1;
int lastRecordIndex = ((lastButtonIndex + 1) * DataPager.PageSize) - 1;

if ( String .Equals (e.CommandName, DataControlCommands.PreviousPageCommandArgument))
{
newStartRowIndex = (firstButtonIndex - 1) * DataPager.PageSize;
if (newStartRowIndex <0)
{
newStartRowIndex = 0;
}
}
else if ( String .Equals (e.CommandName, DataControlCommands.NextPageCommandArgument))
{
newStartRowIndex = lastRecordIndex + 1;
if (newStartRowIndex> _totalRowCount)
{
newStartRowIndex = _totalRowCount - DataPager.PageSize;
}
}
else
{
int pageIndex = Convert .ToInt32 (e.CommandName, CultureInfo.InvariantCulture);
newStartRowIndex = pageIndex * DataPager.PageSize;
}

if (newStartRowIndex! = -1)
{
DataPager.SetPageProperties (newStartRowIndex, DataPager.PageSize, true );
}
}
* This source code was highlighted with Source Code Highlighter .


This is basically everything; the received pager will display the numbers in the right sequence, but it will do it very ugly, because it is actually naked - it does not have properties that define CSS classes, it does not create links for the previous and next pages, etc. But, I hope, it is as clear as it works. The following code represents our pager completely, with all the auxiliary functions and properties.

public class ReversedNumericPagerField: DataPagerField
{
private int _startRowIndex;
private int _maximumRows;
private int _totalRowCount;

public ReversedNumericPagerField ()
{
}

public int ButtonCount
{
get
{
object o = ViewState [ "ButtonCount" ];
if (o! = null )
{
return ( int ) o;
}
return 5;
}
set
{
if ( value <1)
{
throw new ArgumentOutOfRangeException ( "value" );
}
if ( value ! = ButtonCount)
{
ViewState [ "ButtonCount" ] = value ;
OnFieldChanged ();
}
}
}

public ButtonType ButtonType
{
get
{
object o = ViewState [ "ButtonType" ];
if (o! = null )
return (ButtonType) o;
return ButtonType.Link;
}
set
{
if ( value <ButtonType.Button || value > ButtonType.Link)
{
throw new ArgumentOutOfRangeException ( "value" );
}
if ( value ! = ButtonType)
{
ViewState [ "ButtonType" ] = value ;
OnFieldChanged ();
}
}
}

public string CurrentPageLabelCssClass
{
get
{
object o = ViewState [ "CurrentPageLabelCssClass" ];
if (o! = null )
{
return ( string ) o;
}
return String .Empty;
}
set
{
if ( value ! = CurrentPageLabelCssClass)
{
ViewState [ "CurrentPageLabelCssClass" ] = value ;
OnFieldChanged ();
}
}
}

public string NextPageImageUrl
{
get
{
object o = ViewState [ "NextPageImageUrl" ];
if (o! = null )
{
return ( string ) o;
}
return String .Empty;
}
set
{
if ( value ! = NextPageImageUrl)
{
ViewState [ "NextPageImageUrl" ] = value ;
OnFieldChanged ();
}
}
}

public string NextPageText
{
get
{
object o = ViewState [ "NextPageText" ];
if (o! = null )
{
return ( string ) o;
}
return "→" ;
}
set
{
if ( value ! = NextPageText)
{
ViewState [ "NextPageText" ] = value ;
OnFieldChanged ();
}
}
}

public string NextPreviousButtonCssClass
{
get
{
object o = ViewState [ "NextPreviousButtonCssClass" ];
if (o! = null )
{
return ( string ) o;
}
return String .Empty;
}
set
{
if ( value ! = NextPreviousButtonCssClass)
{
ViewState [ "NextPreviousButtonCssClass" ] = value ;
OnFieldChanged ();
}
}
}

public string NumericButtonCssClass
{
get
{
object o = ViewState [ "NumericButtonCssClass" ];
if (o! = null )
{
return ( string ) o;
}
return String .Empty;
}
set
{
if ( value ! = NumericButtonCssClass)
{
ViewState [ "NumericButtonCssClass" ] = value ;
OnFieldChanged ();
}
}
}

public string PreviousPageImageUrl
{
get
{
object o = ViewState [ "PreviousPageImageUrl" ];
if (o! = null )
{
return ( string ) o;
}
return String .Empty;
}
set
{
if ( value ! = PreviousPageImageUrl)
{
ViewState [ "PreviousPageImageUrl" ] = value ;
OnFieldChanged ();
}
}
}

public string PreviousPageText
{
get
{
object o = ViewState [ "PreviousPageText" ];
if (o! = null )
{
return ( string ) o;
}
return "←" ;
}
set
{
if ( value ! = PreviousPageText)
{
ViewState [ "PreviousPageText" ] = value ;
OnFieldChanged ();
}
}
}

public bool RenderNonBreakingSpacesBetweenControls
{
get
{
object o = ViewState [ "RenderNonBreakingSpacesBetweenControls" ];
if (o! = null )
{
return ( bool ) o;
}
return true ;
}
set
{
if ( value ! = RenderNonBreakingSpacesBetweenControls)
{
ViewState [ "RenderNonBreakingSpacesBetweenControls" ] = value ;
OnFieldChanged ();
}
}
}

private void AddNonBreakingSpace (DataPagerFieldItem container)
{
if (RenderNonBreakingSpacesBetweenControls)
{
container.Controls.Add ( new LiteralControl ( "" ));
}
}

protected override DataPagerField CreateField ()
{
return new ReversedNumericPagerField ();
}

public override void HandleEvent (CommandEventArgs e)
{
int newStartRowIndex = -1;
int currentPageIndex = _startRowIndex / DataPager.PageSize;
int firstButtonIndex = (_startRowIndex / (ButtonCount * DataPager.PageSize)) * ButtonCount;
int lastButtonIndex = firstButtonIndex + ButtonCount - 1;
int lastRecordIndex = ((lastButtonIndex + 1) * DataPager.PageSize) - 1;

if ( String .Equals (e.CommandName, DataControlCommands.PreviousPageCommandArgument))
{
newStartRowIndex = (firstButtonIndex - 1) * DataPager.PageSize;
if (newStartRowIndex <0)
{
newStartRowIndex = 0;
}
}
else if ( String .Equals (e.CommandName, DataControlCommands.NextPageCommandArgument))
{
newStartRowIndex = lastRecordIndex + 1;
if (newStartRowIndex> _totalRowCount)
{
newStartRowIndex = _totalRowCount - DataPager.PageSize;
}
}
else
{
int pageIndex = Convert .ToInt32 (e.CommandName, CultureInfo.InvariantCulture);
newStartRowIndex = pageIndex * DataPager.PageSize;
}

if (newStartRowIndex! = -1)
{
DataPager.SetPageProperties (newStartRowIndex, DataPager.PageSize, true );
}
}

private Control CreateNumericButton ( string buttonText, string commandArgument, string commandName)
{
IButtonControl button;

switch (ButtonType)
{
case ButtonType.Button:
button = new Button ();
break ;
case ButtonType.Link:
default :
button = new LinkButton ();
break ;
}

button.Text = buttonText;
button.CommandName = commandName;
button.CommandArgument = commandArgument;

WebControl webControl = button as WebControl;
if (webControl! = null &&! String .IsNullOrEmpty (NumericButtonCssClass))
{
webControl.CssClass = NumericButtonCssClass;
}

return button as Control;
}

private Control CreateNextPrevButton ( string buttonText, string commandName, string commandArgument, string imageUrl)
{
IButtonControl button;

switch (ButtonType)
{
case ButtonType.Link:
button = new LinkButton ();
break ;
case ButtonType.Button:
button = new Button ();
break ;
case ButtonType.Image:
default :
button = new ImageButton ();
((ImageButton) button) .ImageUrl = imageUrl;
((ImageButton) button) .AlternateText = HttpUtility.HtmlDecode (buttonText);
break ;
}
button.Text = buttonText;
button.CommandName = commandName;
button.CommandArgument = commandArgument;

WebControl webControl = button as WebControl;
if (webControl! = null &&! String .IsNullOrEmpty (NextPreviousButtonCssClass))
{
webControl.CssClass = NextPreviousButtonCssClass;
}

return button as Control;
}

public override void CreateDataPagers (DataPagerFieldItem container, int startRowIndex, int maximumRows, int totalRowCount, int fieldIndex)
{
_startRowIndex = startRowIndex;
_maximumRows = maximumRows;
_totalRowCount = totalRowCount;

CreateDataPagersForCommand (container, fieldIndex);
}

private void CreateDataPagersForCommand (DataPagerFieldItem container, int fieldIndex)
{
int currentPageIndex = _startRowIndex / _maximumRows;
int firstButtonIndex = (_startRowIndex / (ButtonCount * _maximumRows)) * ButtonCount;
int lastButtonIndex = firstButtonIndex + ButtonCount - 1;
int lastRecordIndex = ((lastButtonIndex + 1) * _maximumRows) - 1;
int totalPages = ( int ) Math .Ceiling (( double ) _totalRowCount / _maximumRows);

if (firstButtonIndex! = 0)
{
container.Controls.Add (CreateNextPrevButton (PreviousPageText, DataControlCommands.PreviousPageCommandArgument, fieldIndex.ToString (CultureInfo.InvariantCulture), PreviousPageImageUrl));
AddNonBreakingSpace (container);
}

for ( int i = 0; i <ButtonCount && _totalRowCount> ((i + firstButtonIndex) * _maximumRows); i ++)
{
if (i + firstButtonIndex == currentPageIndex)
{
Label pageNumber = new Label ();
pageNumber.Text = (totalPages - i - firstButtonIndex) .ToString (CultureInfo. InvariantCulture);
if (! String .IsNullOrEmpty (CurrentPageLabelCssClass))
{
pageNumber.CssClass = CurrentPageLabelCssClass;
}
container.Controls.Add (pageNumber);
}
else
{
container.
}
AddNonBreakingSpace (container);
}

if (lastRecordIndex <_totalRowCount - 1)
{
AddNonBreakingSpace (container);
container.Controls.Add (CreateNextPrevButton (NextPageText, DataControlCommands.NextPageCommandArgument, fieldIndex.ToString (CultureInfo.InvariantCulture), NextPageImageUrl));
AddNonBreakingSpace (container);
}
}
} * This source code was highlighted with Source Code Highlighter .


This class performs the function laid on it and displays the numbers in reverse order. However, it was created in haste and perhaps the math in the CreateDataPagersForCommand function is not entirely optimal. In any case, I hope that this article will help someone.

And further. Since this is my first article on programming and, accordingly, the first article on Habré, it was not easy for me to pick up the necessary Russian programmer terminology, as I use English in everyday practice. So write comments, correct. And advise the English-Russian dictionary programmer :)

Source: https://habr.com/ru/post/28441/


All Articles