Dapfor .Net Grid Introduction

Part1: Data Types
Part2: Data Binding
Part3: Event-driven model
Part4: Data formatting and presentation
Part5: Threadsafety
Part6: Threadsafe BindingList
Part7: Appearance and painting
Part8: Cell highlighting
Part9: Data filtering
Part10: Data sorting
Part11: Data grouping
Part12: Headers and columns
Part13: Editors
Part14: Performance. Practical recommendations
Part15: Real-time blotter
Part16: Currency converter
Part17: Instrument browser
Part18: Order book
Part19: Basket Viewer



.Net Grid Tutorial Part9: Data sorting

Generally, data sorting information is contained in grid headers. Users may set single or multiple sorting in one or multiple headers. On programming level, sorting looks as follows:


//Get top-level header
Header header = grid.Headers[0];

header["Product"].SortDirection = SortDirection.Ascending;
header["Price"].SortDirection = SortDirection.Descending;

Setting Column.Sortable property to false is sufficient to enable software-only sorting preventing users from modifying it via UI.

By default, during sorting row data is compared using IComparable interface. In other words, in most cases data objects return simple values (int, double, string, DateTime, TimeSpan, etc.) in their properties. The IComparable interface is implemented in all these object types. Thanks to this approach, the grid can compare not only rows with data of similar type but also data of completely different types. It is also important that sorting with IComparable compares values instead of formatted stocks that are displayed in grid cells. To show the importance of this, we shall provide an example of sorting by date represented with DateTime type.


class DateExample
{
private readonly DateTime _date;

public DateExample(DateTime date)
{
_date = date;
}

public DateTime Date
{
get { return _date; }
}
}

//Create header and column
grid.Headers.Add(new Header());
grid.Headers[0].Add(new Column("Date"));

//Configure the header
grid.Headers[0].StretchMode = ColumnStretchMode.All;
grid.Headers[0][0].SortDirection = SortDirection.Ascending;
grid.Headers[0]["Date"].Format = new StringFormat("d", new CultureInfo("fr-FR"));

//Populate grid with random data
Random random = new Random();
BindingList<DateExample> source = new BindingList<DateExample>();
for (int i = 0; i < 6; ++i)
{
source.Add(new DateExample(DateTime.Now + TimeSpan.FromDays(random.Next(1000))));
}
grid.DataSource = source;
Unformatted values sorting

A programmer may also set his own sorting rules using ICustomSort to set specific sorting behavior.


//An implementation ICustomSort that sorts values by day of week
class DayOfWeekCustomSort : ICustomSort
{
public int Compare(Row row1, Row row2, string fieldId, object value1, object value2, int defaultResult)
{
if(fieldId == "Date")
{
DateTime d1 = (DateTime) value1;
DateTime d2 = (DateTime) value2;

if (d1.DayOfWeek != d2.DayOfWeek)
{
return d1.DayOfWeek > d2.DayOfWeek ? 1 : -1;
}
}
return defaultResult;
}
}

//Setup custom sorting
grid.CustomSort = new DayOfWeekCustomSort();
Custom sorting

The grid can be sorted by one or multiple columns. It is possible to use up to five columns per header simultaneously and to use multiple headers.

Simultaneous sorting, filtering and grouping

One of the most powerful grid capabilities is support of both independent and simultaneous processing of data sorting, grouping and filtering. A programmer may use any combination of these operations without any overhead keeping business logic code simple and clear.

Sorting dynamic data

Row.Update() method is called to change row position during sorting. The grid calculates new row position basing on the previously described IComparable and ICustomSort interfaces. When event-driven model is used (i.e. when data objects implement INotifyPropertyChanged interface or are contained in IBindingList), the grid gets relevant PropertyChanged and ListChanged notifications. When the grid gets such notification, it performs a mandatory check for the need to synchronize the calling thread with the thread where the control works, calling Control.Invoke() / Control.BeginInvoke() method, if required. After that the grid automatically calls Row.Update() method. This approach enables complete separation of the data layer from the presentation layer and prevents calling GUI methods from non-GUI thread. It is interesting that in the event-driven model the same data can be presented in different grids with different sorting, grouping, filtering and hierarchy. Changes of data objects or IBindingList collections result in notifications to each of the controls that will perform required actions depending on their configuration, user preferences and data structure.

Sorting and docked rows

In the grid a programmer may set rows that will be always located above or below at their hierarchy level without regard to user preferences or sorting. This can be done by setting Row.DockStyle property as shown in the example.


/// Dock some rows to the top and the bottom
grid.Rows[10].Dock = RowDockStyle.Top;
grid.Rows[11].Dock = RowDockStyle.Bottom;

Sequential setting of this property for multiple rows attaches them to top or bottom of the grid depending on programmer's preferences. When all rows are docked, sorting will not be performed at this hierarchy level. Docked rows can be used to display important orgeneral information (e.g. average/maximum/minimum value of grid data).  Objects in docked rows may be of arbitrary type and may have different types than those of objects in regular scrollable rows.

Performance

The grid is optimized for working with dynamic data changed in real time. Resource consumption stays at the minimum irrespective of the methods of filling grid with data, data type and hierarchy level. For example, highly efficient integrated algorithms enable a grid with 5 000 rows to perform over 5 000 sorting operations per second. Here sorting means calling Row.Update() method followed by row relocation. If after calling Row.Update() method the row doesn’t move or moves outside visible area of the grid, CPU resource consumption is much lower.

Returning to the event-driven model, we can say that objects of the model may freely send notifications via INotifyPropertyChanged/IBindingList interfaces without any threat of the application hanging or performance dropping if a user turns on sorting for dynamic real-time data.