Introduction
One of my most used ASP.NET control is DataGrid
. Presenting data from tables is very easy and nice with only one exception - the page navigator. Showing only page numbers or only two links (to previous and next page) is insufficient for me, and completely inexplicable for my clients. So I decided to create DataGridNavigator
Server Control, the main task of which is to generate the page navigator for DataGrid
object. The navigator can have the following buttons:
- to jump to the first / last page (text or image)
- to jump to the previous / next page (text or image)
- to jump to the page no. ... (text only :) )
The navigator control is rendered independently of the DataGrid
control, and you can bind multiple navigators to one DataGrid
.
The control, properties and editors
The control inherits from the WebControl
class (in ASP.NET, the control can inherit directly or indirectly from either the Control
or WebControl
base class) and is rendered in the span
HTML tag.
Some of the properties have been assigned specified editors, like System.Web.UI.Design.ImageUrlEditor
(for the xxxButtonImageUrl
properties). This editor shows "Select image" dialog window (the same which is used in the ImageUrl
property of the HyperLink
control). All we have to do is to set the property's EditorAttribute
:
[Category("Buttons"),
DefaultValue(""),
Description("First button's image URL."),
EditorAttribute(typeof(System.Web.UI.Design.ImageUrlEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public string FirstButtonImageUrl
{
get { object o = ViewState["FirstButtonImageUrl"];
return (o==null) ? String.Empty : (string) o; }
set { ViewState["FirstButtonImageUrl"] = value.Trim(); }
}
There's also a special editor for the DataGridName
property, which shows a list of all DataGrid
objects on the page. You can find the editor's implementation in the DataGridNavigatorEditor.cs file.
The SelectDataGrid editor class
All editor classes should derive from the UITypeEditor
class. One of the methods you should implement is the GetEditStyle
, which returns editor type:
DropDown
- dropdown dialog
Modal
- modal window
None
- no UI.
For example:
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
The next method to implement is EditValue
, in which the editor window/control is created. In the case of the dropdown editor, we can use the ListBox
control. As you can see in the DataGridNavigatorEditor.cs file, I've created an abstract class ObjectListEditor
which implements the base functionality for that kind of editors. The class has InsertItems
method used to fill the list with items. The main editor is implemented in the SelectDataGrid
class, which derives from the ObjectListEditor
class. Here is the implementation of the InsertItems
method:
protected override void InsertItems(ListBox.ObjectCollection items,
ComponentCollection components)
{
for (int i = 0; i < components.Count; i++)
if (components[i] is System.Web.UI.WebControls.DataGrid)
items.Add(((System.Web.UI.WebControls.DataGrid)components[i]).ID);
}
As you can see, there is an iteration loop through the page's components. Every DataGrid
component is added into the list items collection. If, for some reason, you would like to add other components, just change the conditional test.
Page's components collection (the method's component
argument) is provided by the ITypeDescriptorContext
interface (precisely by the Container.Components
property), as you can see in the EditValue
method implementation (in the ObjectListEditor
class).
To assign the editor to the property, just set the EditorAttribute
:
[Category("Data"),
DefaultValue(""),
Description("DataGrid name to which connect the control."),
Editor(typeof(mavus74.net.Editors.SelectDataGrid),
typeof(System.Drawing.Design.UITypeEditor))]
public string DataGridName
{
get { object o = ViewState["DataGridName"];
return (o==null) ? String.Empty : (string) o; }
set { ViewState["DataGridName"] = value.Trim(); }
}
The PostBack event
The main control's event is Command
, based on the CommandEventHandler
, which is invoked by the button click.
If control uses PostBack events, it should implement the IPostBackEventHandler
interface. The Command
event property is implemented as follows:
private static readonly object EventCommand = new object();
...
[Category("Action"),
Description("OnCommand event")]
public event CommandEventHandler Command
{
add { Events.AddHandler(EventCommand, value); }
remove { Events.RemoveHandler(EventCommand, value); }
}
This implementation relies on the C# event property construct. Now, it's time to implement the IPostBackEventHandler.RaisePostBackEvent
method. In this method, you have to decide which event was invoked (the eventArgument
has the event name):
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
switch (eventArgument)
{
case "FIRST":
OnCommand(new CommandEventArgs("FIRST", 0));
return;
case "PREV":
OnCommand(new CommandEventArgs("PREV",
(dgData.CurrentPageIndex == 0) ? 0 : dgData.CurrentPageIndex - 1));
return;
case "NEXT":
OnCommand(new CommandEventArgs("NEXT",
(dgData.CurrentPageIndex == dgData.PageCount-1) ? dgData.PageCount-1 :
dgData.CurrentPageIndex + 1));
return;
case "LAST":
OnCommand(new CommandEventArgs("LAST", dgData.PageCount - 1));
return;
}
if (eventArgument.StartsWith("PAGE_"))
{
int i = Convert.ToInt32( eventArgument.Remove(0, 5));
OnCommand(new CommandEventArgs("PAGE", i));
}
}
The OnCommand
implementation:
protected virtual void OnCommand(CommandEventArgs e)
{
CommandEventHandler clickHandler = (CommandEventHandler)Events[EventCommand];
if (clickHandler != null) clickHandler(this, e);
}
And the main point - rendering postback event code. The control renders a group of HTML links with post back event. The code is generated by the Page.GetPostBackClientHyperlink
method, which gets two parameters:
control
- the server control which will process the postback
argument
- the event's argument, which is passed to the RaisePostBackEvent
method.
writer.AddAttribute(HtmlTextWriterAttribute.Href,
Page.GetPostBackClientHyperlink(this, "FIRST"));
The GetPostBackClientHyperlink
method calls the GetPostBackEventReferece
and appends 'javascript: ' text at the beginning.
Using the control
First, you need to bind the control with DataGrid
by the DataGridName
property. You also need to implement the Command
event, for example:
private void DataGridNavigator1_Command(object sender,
System.Web.UI.WebControls.CommandEventArgs e)
{
dgDane.CurrentPageIndex = (int) e.CommandArgument;
bindData();
}
You also can set button's text, images and CSS class (ButtonsCssClass
property). If you don't specify button's ImageUrl
, than text link is rendered. Otherwise, image button is rendered. By setting the ShowPageButtons
, you can decide to render page numbers. PageButtonCount
property decides how many links to render (value between 3 and 21).
Points of Interest
This control shows how to create a server control with post back events handler, assign the property editor and how to create easy property editor.
TO-DO
- specialized event handler instead of the
CommandEventHandler
. This handler can provide 2 properties: the DataGrid
control and PageNumber
(now it's CommandArgument
).
DisabledImageUrl
- image used when the link is disabled.