In this tutorial we shall demonstrate how easy it is to create a reusable Ordered ListControl in
Gaia Ajax. We will also use generics so that it applies to all ListControls like ListBox, DropDownList, CheckBoxList, RadioButtonList, etc. And because Gaia Ajax is a server side Ajax framework we're going to
write the control in C#. The control can be used from any .NET managed
language. As one of our users pointed out: Gaia is the
"Chuck Norris of
Ajax Frameworks for ASP.NET"

First off, let's declare our OrderedListControl
public class OrderedListControl<T> : Control
where T: System.Web.UI.WebControls.ListControl, new() {}
Here you will notice that we use the generic type parameter T which is constrained to only be concerned with ListControls. The new() keyword signals that it requires a parameterless constructor.
As component developers we should be very concerned about the API, but for this limited example we will implement only a small feature set. To make the API compact we use encapsulation with a proxy to the listcontrol. In addition we're adding the up and down buttons. If you want to extend/change the API yourself feel free to do so.
One obvious way to extend the listbox would be to attach AspectKey to the ListBox and explicitly listen to KEY_UP and KEY_DOWN. Then your ordered listbox will be easy to sort based on your needs.
With the requirements we defined, we implement the following members of the class
private readonly Panel _panelContainer = new Panel();
private readonly T _orderedListControl = new T();
private readonly Button _upButton = new Button();
private readonly Button _downButton = new Button();
If the up/down buttons are clicked directly we will also fire the OrderChanged event which we define as follows
public event EventHandler OrderChanged;
Because most of the implementation is internal we can decide ourselves what parts we want to expose (thereby encapsulating the internals). Let's give the user the ability to set the Height, Width Enabled, SelectedIndex and the Items. The nice thing with the composition pattern is that we can just forward the properties we want to publish. This is the pattern we've used to create many of the advanced controls in the Gaia Ajax library.
public bool Enabled
{
get { return _orderedListControl.Enabled; }
set
{
_orderedListControl.Enabled = value;
_upButton.Enabled = value;
_downButton.Enabled = value;
}
}
public System.Web.UI.WebControls.Unit Width
{
get { return _orderedListControl.Width; }
set { _orderedListControl.Width = value; }
}
public System.Web.UI.WebControls.Unit Height
{
get { return _orderedListControl.Height; }
set { _orderedListControl.Height = value; }
}
public System.Web.UI.WebControls.ListItemCollection Items
{
get { return _orderedListControl.Items; }
}
public int SelectedIndex
{
get { return _orderedListControl.SelectedIndex; }
}
The next thing we need to do is to override a few functions from the base class. We'll initialize the component in a function called InitializeComponent and add the controls to the collection in the CreateChildControls() function. Notice how all controls are added to the panelContainer control and then the panelContainer control is added to the Controls collection.
protected override void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
protected override void CreateChildControls()
{
_panelContainer.Controls.Add(_orderedListControl);
_panelContainer.Controls.Add(new HtmlGenericControl("br"));
_panelContainer.Controls.Add(_upButton);
_panelContainer.Controls.Add(_downButton);
Controls.Add(_panelContainer);
base.CreateChildControls();
}
protected override void OnLoad(EventArgs e)
{
EnsureChildControls();
base.OnLoad(e);
}
Now let's move over to implementation. We defined a function called InitializeComponent() that will initialize the controls with default properties and wire up some event handlers. In the Click handler for the up button we check that we have a valid selection before we perform the MoveUp() functionality and then fire the OrderChanged event because it was a user inited action. A lot of this code is the same for the Down button. You can see it here ...
private void InitializeComponent()
{
_upButton.Text = "Up";
_upButton.Click += delegate
{
if (_orderedListControl.SelectedIndex <= 0)
return;
MoveUp();
if (OrderChanged != null)
OrderChanged(this, EventArgs.Empty);
};
_downButton.Text = "Down";
_downButton.Click += delegate
{
if (_orderedListControl.SelectedIndex == -1 ||
_orderedListControl.SelectedIndex == _orderedListControl.Items.Count - 1)
return;
MoveDown();
if (OrderChanged != null)
OrderChanged(this, EventArgs.Empty);
};
}
The final thing to do before we're ready to consume this OrderedListControl is to implement the MoveUp() and MoveDown() functions. I've made these public so you can move items up/down programmatically in your code. Notice that there's no defensive checking for SelectedIndex in these public functions, but in the Up/Down click handlers we have them.
The code is as follows
public void MoveUp()
{
int oldIndex = _orderedListControl.SelectedIndex;
var itemAbove = _orderedListControl.Items[_orderedListControl.SelectedIndex - 1];
_orderedListControl.Items.Remove(itemAbove);
_orderedListControl.Items.Insert(oldIndex, itemAbove);
_orderedListControl.SelectedIndex = oldIndex - 1;
}
public void MoveDown()
{
int oldIndex = _orderedListControl.SelectedIndex;
var itemBelow = _orderedListControl.Items[_orderedListControl.SelectedIndex + 1];
_orderedListControl.Items.Remove(itemBelow);
_orderedListControl.Items.Insert(oldIndex, itemBelow);
_orderedListControl.SelectedIndex = oldIndex + 1;
}
Now the control is ready to be consumed. We can add the control programmatically to the page and populate it with some items. Notice that I am passing the type ListBox in this case.
using System;
using Unit=System.Web.UI.WebControls.Unit;
using Gaia.WebWidgets;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var listControl = new OrderedListControl<ListBox>();
listControl.Width = Unit.Pixel(100);
listControl.Height = Unit.Pixel(100);
listControl.Items.Add("First");
listControl.Items.Add("Second");
listControl.Items.Add("Third");
listControl.Items.Add("Fourth");
Form.Controls.Add(listControl);
}
}
Sometimes it's inconvenient to use controls that require you to to pass in the type to the declaration. One possible solution to this could be to create direct implementations of the various controls you need. For example like this
public class OrderedListBox : OrderedListControl<ListBox> {}
Now you can go ahead and use the OrderedListBox directly. If you want to use a different listcontrol, just swap out the type parameter or create a specific implementation and go ahead and use that control.
Here's the exact same code using the RadioButtonList instead.
The PropertyStateManager in Gaia makes sure that the Ajax calls are highly effecient on the wire. Here's the total footprint for one simple change.
That concludes this tutorial. We hope you like it and feel free to use the code if you want in your own projects.
Download Code : OrderedListControl.cs (3.81 kb)