Using the Code
The first point to note is I haven't done any forms designer compatibility work with this control. I tend to use the designer for laying the controls out and then go straight into the code to complete the rest of the work, hence no designer support for Superlist in terms of adding Columns and configuring them etc. To use the control you'll need of course to add it to a Form or UserControl, then in code you will need to create its columns as in the example below:
public SuperListTestForm()
{
InitializeComponent();
Column surnameColumn = new Column( "surname", "Surname", 120,
delegate( object item ) { return ((Person)item).Surname; } );
Column firstnameColumn = new Column( "firstname", "Firstname", 120,
delegate( object item ) { return ((Person)item).Firstname; } );
Column phoneColumn = new Column( "phone", "Phone", 100, delegate(
object item ) { return ((Person)item).Phone; } );
Column cityColumn = new Column( "city", "City", 60, delegate(
object item ) { return ((Person)item).City; } );
Column stateColumn = new Column( "state", "State", 70, delegate(
object item ) { return ((Person)item).State; } );
Column dateColumn = new Column( "date", "Date", 110, delegate(
object item ) { return ((Person)item).Date.ToString(); } );
dateColumn.GroupItemAccessor = new ColumnItemValueAccessor(
GroupValueFromItem );
dateColumn.MoveBehaviour = Column.MoveToGroupBehaviour.Copy;
dateColumn.GroupSortOrder = SortOrder.Descending;
surnameColumn.SortOrder = SortOrder.Ascending;
_superList.Columns.Add( firstnameColumn );
_superList.Columns.Add( phoneColumn );
_superList.Columns.Add( stateColumn );
_superList.Columns.Add( cityColumn );
_superList.Columns.Add( dateColumn );
_superList.Columns.GroupedItems.Add( dateColumn );
_superList.Columns.GroupedItems.Add( stateColumn );
_superList.SelectedItems.DataChanged +=
new SelectedItemsCollection.DataChangedHandler(
SelectedItems_DataChanged );
int tickStart = Environment.TickCount;
const int iterationCount = 1; // Change this if you want to increase
// the number of items in the list
for( int i = 0; i < iterationCount; i++ )
{
_superList.Items.AddRange( Person.GetData() );
}
}
Column Object
The column object is where most of your work lays in terms of getting the control up and running, you create it with the following constructor:
public Column( string name, string caption, int width,
ColumnItemValueAccessor columnItemValueAccessor )
The
name parameter is used to uniquely identify the Column for serialisation etc. The
columnItemValueAccessor parameter is a delegate you need to supply that is used to return back the object to render in the associated cell (normally a string):
public delegate object ColumnItemValueAccessor( object rowItem );
Once you've defined your columns you can add them to the list via the Columns property.
Grouping
Grouping is as simple as adding the column to the
Columns.GroupedItems property. By default the value used to group on is the same as the
columnItemValueAccessor parameter passed in the
Column constructor. However, you can override this by supplying a new
ColumnItemValueAccessor delegate to the
Column.GroupItemAccessor property. In my example program I override this property to supply the grouped Date column values 'Today', 'Yesterday', 'Last Week' etc.
Sorting
The value returned by the
columnItemValueAccessor parameter by default must support
IComparable otherwise an exception will be thrown when sorting is applied. Alternatively you can override
Column.Comparitor and
Column.GroupComparitor if you want to handle comparisons manually. You set the initial sorting style of a
Column by setting the
Column.SortOrder property. There is also a
Column.GroupSortOrder property for setting the group ordering when in grouped mode.
Behaviour
In the past one of the performance bottelnecks that I've seen is where an application will add lots of items to a control causing it to visually slow down, as the control updates itself each time a new item is added. These problems are easy to fix and normally involve telling the control to disable rendering whilst the adding operation is in progress. We get around these potential problems with the Superlist as it processes changes to the list in the background, if you want the UI to syncronise with the changes straight away then you can call
ListControl.Items.SynchroniseWithUINow(). By default the background processing is done on application idle, you can change this so part of the processing is done in a separate thread by setting
ListControl.Items.ProcessingStyle = BinaryComponents.SuperList.ItemLists.ProcessingStyle.Thread, this will move the sorting part of processing over to a thread. Bare in mind that any of the properties on the
Column object maybe called in the separate thread when in threading mode is set.
Design
Each visual aspect of the control like the header, rows and cells are all derived from
Section, a
Section is semantically similar to
Control but without the latter's resource overhead. It has a rectangular area, focus, mouse, and drag drop support.
The
Sections are contained by the
SectionContainer object which passes keyboard, mouse and drag drop information to them. In the case of the Superlist the
ListControl (not in the diagram above) derives from
SectionContainerControl, when the
ListControl is constructed it adds the
CustomiseListSection and the
ListSection to its canvas. The
CustomiseListSection contains the grouping columns as well as a
ToolStrip for the lists commands. The
ListSection contains the list header, groups and rows.
Customising
The
ListControl exposes the property
ListControl.SectionFactory, when set with your own
SectionFactory you can override any of the lists
Sections. We use this in
FeedGhost to give the List a glossy look (click on image for a larger pic):
When overriding a section the two main methods you'll be interested in will be
void Section.Layout( GraphicsSettings gs, Size maximumSize ) and
void Section.Paint( GraphicsSettings gs, Rectangle clipRect ). Layout is called when the parent Section wants to know the size and height of your
Section. To see an overriden row in my demo if you click on the 'Customize' menu item followed by 'Toggle Row Paint Override' you can see rows that are gradient filled. The code can be seen below taken from the demo applications
Form:
#region Example of overriding rows
/// <summary />
/// Storage area for the row override.
/// </summary />
private RowOverrideExample _rowOverride;
private void toggleRowPaintingOverrideToolStripMenuItem_Click( object sender,
EventArgs e )
{
if( _rowOverride == null )
{
//
// Start overrride.
_rowOverride = new RowOverrideExample( _superList );
}
else
{
//
// Clear override.
_rowOverride.Dispose();
_rowOverride = null;
}
}
/// <summary />
/// Example of overriding rows giving a gradient fill look.
/// </summary />
private class RowOverrideExample: IDisposable
{
public RowOverrideExample(
BinaryComponents.SuperList.ListControl listControl )
{
_oldFactory = listControl.SectionFactory; // store old factory as we
// want to leave as we came.
_listControl = listControl;
//
// Replace the current SectionFactory with our override.
listControl.SectionFactory = new MySectionFactory(); //
_listControl.LayoutSections();
}
public void Dispose()
{
if( _oldFactory != null ) // put things back as they were
{
_listControl.SectionFactory = _oldFactory;
_listControl.LayoutSections();
}
}
private class MySectionFactory : SectionFactory
{
public override RowSection CreateRowSection(
BinaryComponents.SuperList.ListControl listControl,
RowIdentifier rowIdenifier,
HeaderSection headerSection,
int position )
{
return new MyRowSection( listControl, rowIdenifier,
headerSection, position );
}
}
private class MyRowSection : RowSection
{
public MyRowSection(
BinaryComponents.SuperList.ListControl listControl,
RowIdentifier rowIdentifier,
HeaderSection headerSection,
int position )
: base( listControl, rowIdentifier, headerSection, position )
{
_position = position;
}
public override void PaintBackground( Section.GraphicsSettings gs,
Rectangle clipRect )
{
Color from, to;
if( _position % 2 == 0 )
{
from = Color.White;
to = Color.LightBlue;
}
else
{
to = Color.White;
from = Color.LightBlue;
}
using( LinearGradientBrush lgb = new LinearGradientBrush(
this.Rectangle,
from,
to,
LinearGradientMode.Horizontal ) )
{
gs.Graphics.FillRectangle( lgb, this.Rectangle );
}
}
public override void Paint( Section.GraphicsSettings gs,
Rectangle clipRect )
{
base.Paint( gs, clipRect );
}
private int _position;
}
private BinaryComponents.SuperList.ListControl _listControl;
private SectionFactory _oldFactory;
}
#endregion
On the customisation Section there is a ToolStrip that is created inside the ToolStripOptionsToolbarSection object:
You can also add your own
ToolStripItems to it by overriding the
ToolStripOptionsToolbarSection class and passing your version in via your own
SectionFactory. Alternatively you could replace the whole area if you wanted by deriving from
OptionsToolbarSection; we do this in
FeedGhost using our own
Ribbon strip instead.
Loading and Saving State
Superlist's state can be saved and loaded with
void ListControl.SerializeState( System.IO.TextWriter writer ) and
void ListControl.DeSerializeState( System.IO.TextReader reader ) respectively.
Saving Example
using( System.IO.TextWriter textWriter = File.CreateText( ofd.FileName ) )
{
_superList.SerializeState( textWriter );
}
Loading Example
using( System.IO.TextReader textReader = File.OpenText( fileName ) )
{
_superList.DeSerializeState( textReader );
}
Wrapping it Up
I hope you find this control useful and if you have any ideas, bugs or suggestions feel free leave a message here. I'll also be posting updates here as well as on my company site
BinaryComponents.