Skip to main content Skip to footer

MultiColumn Sort in C1TrueDBGrid

There comes a time where you may find yourself wanting to sort multiple columns based on a sort condition to display data. The C1Flexgrid has an "AllowSorting" property, which defines whether the sorting is to be applied on Single Columns or MultipleColumns.

In C1TrueDbGrid this can be achieved by disabling the in-built sorting and applying Custom sorting. The first step is to bind the C1TrueDbGrid with Orders, table of C1NwindDB, which is available with the ComponentOne WinForms Edition.

To implement Custom sorting, we will need handle the sorting ourselves in the grid's HeadClick event. We can do this by setting AllowSort to False and make the column headers behave like buttons. The DataColumn.Tag property of grid's DisplayColumn will store the SortDirection information. Also, we need an enum 'SortDir' to save the sort direction (Ascending/Descending/None) and a static variable sortCondition which will store the sort condition. In addition, we will use 2 variables to store the sorting images. The code looks like this:

//SortDir  
 public enum SortDir  
 {  
 None,  
 Asc,  
 Desc  
 }  

 private Bitmap _sortup;  
 private Bitmap _sortdn;  
 private static string sortCondition = string.Empty  
private void Form1_Load(object sender, EventArgs e)  
 {  
 OleDbConnection1.ConnectionString = GetModifiedConnectionString(this.OleDbConnection1.ConnectionString);  
 OleDbDataAdapter1.Fill(this.dataSet11);  
 C1TrueDBGrid1.DataSource = this.dataSet11.Tables[0].DefaultView;  
 // we're going to handle the sorting ourselves in the grids headclick event  
 this.C1TrueDBGrid1.AllowSort = false;  

 foreach (C1.Win.C1TrueDBGrid.C1DisplayColumn dc in C1TrueDBGrid1.Splits[0].DisplayColumns)  
 {  
 // make the column headers act like buttons  
 dc.ButtonHeader = true;  
 // default sort order  
 dc.DataColumn.Tag = SortDir.None;  
 }  
 _sortdn = new Bitmap("..//..//SortDn.bmp");  
 _sortup = new Bitmap("..//..//SortUp.bmp");  
 _sortdn.MakeTransparent(Color.White);  
 _sortup.MakeTransparent(Color.White);  
 }  


The GetModifiedConnectionString() method here returns the connection string of the database. The function is as follows:

private string GetModifiedConnectionString(string connstring)  
{  
    int dataSource = connstring.IndexOf("Data Source=", 0, StringComparison.OrdinalIgnoreCase);  
    int dataSourceEnd = connstring.LastIndexOf("\\\", connstring.IndexOf(";", dataSource));  
    connstring = connstring.Substring(0, dataSource)   "Data Source="   Environment.GetFolderPath(Environment.SpecialFolder.Personal)   "\\\ComponentOne Samples\\\Common"   connstring.Substring(dataSourceEnd);  
    return connstring;  
}

The next and most important step in our sample is to implement multiple column sorting. To do this we will subscribe C1TruDbgrid's HeadClick event, the event that gets fired whenever the column header is clicked. For Custom Sorting we would need to get the SortCondition for the grid and then apply it. In the beginning we will get the Sort Direction of the column that is clicked.

This information is available in the DataColumn.Tag property. The SortImage is shown for the selected column when actions are steered in this directon and set the image position by setting the DisplayColumn.ForeGroundPicturePosition property. The sort position is again saved in the DataColumn.Tag property. The code should look like following:

C1.Win.C1TrueDBGrid.C1DisplayColumn dc = this.C1TrueDBGrid1.Splits[0].DisplayColumns[e.ColIndex];  
 // new sort order  
 SortDir newsort = (SortDir)dc.DataColumn.Tag;  
 switch (newsort)  
 {  
 case SortDir.None:  
 newsort = SortDir.Asc;  
 dc.HeadingStyle.ForegroundImage = this._sortup; //update image  
 break;  

 case SortDir.Asc:  
 newsort = SortDir.Desc;  
 dc.HeadingStyle.ForegroundImage = this._sortdn; //update image  
 break;  

 case SortDir.Desc:  
 newsort = SortDir.None;  
 dc.HeadingStyle.ForegroundImage = null;  
 break;  
 }  

 // indicators go to the right of text  
 dc.HeadingStyle.ForeGroundPicturePosition = C1.Win.C1TrueDBGrid.ForeGroundPicturePositionEnum.RightOfText;  

 // save the sort state  
 dc.DataColumn.Tag = newsort;  


Once this is done, we need to uncover whether or not the data field already contains a sort criteria in existing sortCondition. If the data field does not have any sort criteria, then we will to check if the sortCondition is empty or not. When the SortCondition is empty we will we set the Sort condition to the DataField, and if it is not, we will append the datafield to the existing sort criteria.

Once this is done we need to specify the sortDirection to the sort criteria. If the datafield has a sort criteria, we will find the position of the data field and check for the sort direction. If the desired sort direction is ASC or DESC, then we will append to the sort condition. If the desired direction is None, then we would simply remove the datafield from the sortCondition. The code will look like following:

//find if data field already has a sort criteria in existing sortCondition  
 int posOfFieldName = sortCondition.IndexOf(dc.DataColumn.DataField);  

 if (posOfFieldName == -1)  
 {  
 //if data field is not found and doesn't have an existing sort criteria  
 if (sortCondition == string.Empty)  
 {  
 sortCondition = dc.DataColumn.DataField + " ";  
 }  
 else  
 {  
 sortCondition += "," + dc.DataColumn.DataField + " ";  
 }  
 sortCondition += dc.DataColumn.Tag.Equals(SortDir.Desc) ? "DESC " : "ASC ";  
 }  
 else  
 {  
 int startPosOfFirstSpace = sortCondition.IndexOf(" ", posOfFieldName + 1);  
 int startPosOfSecondSpace = sortCondition.IndexOf(" ", startPosOfFirstSpace + 1);  
 sortCondition = sortCondition.Remove(posOfFieldName, startPosOfSecondSpace - posOfFieldName + 1);  
 if (newsort != SortDir.None)  
 {  
 sortCondition = sortCondition.Insert(posOfFieldName, dc.DataColumn.DataField.ToString() + " " + (dc.DataColumn.Tag.Equals(SortDir.Desc) ? "DESC " : "ASC "));  
 }  
 if (posOfFieldName > 0 && sortCondition.Length == posOfFieldName)  
 {  
 //if there is no field ahead then remove the previous comma also  
 sortCondition = sortCondition.Remove(posOfFieldName - 1, 1);  

 }  
 else if (sortCondition != string.Empty && sortCondition.Substring(posOfFieldName, 1) == ",")  
 {  
 if (posOfFieldName > 0)  
 {  
 sortCondition = sortCondition.Remove(posOfFieldName, 1);  
 }  
 else if (posOfFieldName == 0)  
 {  
 sortCondition = sortCondition.Remove(posOfFieldName, 1);  
 }  
 }  
 }  


Now the only thing left is to apply the sortCondition to the datasource of C1TdbGrid and our grid will sort accordingly. We can also show the SortCondition in the form's title.

 // sort the grid
this.dataSet11.Tables[0].DefaultView.Sort = sortCondition;
this.Text = "C1TrueDbGrid Sort Condition: " + (sortCondition!=""?sortCondition: "None");

Hence, our HeadClick event will have the following code:

// my own custom sorting when a column header is pressed  
 private void C1TrueDBGrid1_HeadClick(object sender, C1.Win.C1TrueDBGrid.ColEventArgs e)  
 {  
 // get the display column that was clicked  
 {  
 C1.Win.C1TrueDBGrid.C1DisplayColumn dc = this.C1TrueDBGrid1.Splits[0].DisplayColumns[e.ColIndex];  
 // new sort order  
 SortDir newsort = (SortDir)dc.DataColumn.Tag;  
 switch (newsort)  
 {  
 case SortDir.None:  
 newsort = SortDir.Asc;  
 dc.HeadingStyle.ForegroundImage = this._sortup; //update image  
 break;  

 case SortDir.Asc:  
 newsort = SortDir.Desc;  
 dc.HeadingStyle.ForegroundImage = this._sortdn; //update image  
 break;  

 case SortDir.Desc:  
 newsort = SortDir.None;  
 dc.HeadingStyle.ForegroundImage = null;  
 break;  
 }  

 // indicators go to the right of text  
 dc.HeadingStyle.ForeGroundPicturePosition = C1.Win.C1TrueDBGrid.ForeGroundPicturePositionEnum.RightOfText;  

 // save the sort state  
 dc.DataColumn.Tag = newsort;  

 //find if data field already has a sort criteria in existing sortCondition  
 int posOfFieldName = sortCondition.IndexOf(dc.DataColumn.DataField);  

 if (posOfFieldName == -1)  
 {  
 //if data field is not found and doesn't have an existing sort criteria  
 if (sortCondition == string.Empty)  
 {  
 sortCondition = dc.DataColumn.DataField + " ";  
 }  
 else  
 {  
 sortCondition += "," + dc.DataColumn.DataField + " ";  
 }  
 sortCondition += dc.DataColumn.Tag.Equals(SortDir.Desc) ? "DESC " : "ASC ";  
 }  
 else  
 {  
 int startPosOfFirstSpace = sortCondition.IndexOf(" ", posOfFieldName + 1);  
 int startPosOfSecondSpace = sortCondition.IndexOf(" ", startPosOfFirstSpace + 1);  
 sortCondition = sortCondition.Remove(posOfFieldName, startPosOfSecondSpace - posOfFieldName + 1);  
 if (newsort != SortDir.None)  
 {  
 sortCondition = sortCondition.Insert(posOfFieldName, dc.DataColumn.DataField.ToString() + " " + (dc.DataColumn.Tag.Equals(SortDir.Desc) ? "DESC " : "ASC "));  
 }  
 if (posOfFieldName > 0 && sortCondition.Length == posOfFieldName)  
 {  
 //if there is no field ahead then remove the previous comma also  
 sortCondition = sortCondition.Remove(posOfFieldName - 1, 1);  

 }  
 else if (sortCondition != string.Empty && sortCondition.Substring(posOfFieldName, 1) == ",")  
 {  
 if (posOfFieldName > 0)  
 {  
 sortCondition = sortCondition.Remove(posOfFieldName, 1);  
 }  
 else if (posOfFieldName == 0)  
 {  
 sortCondition = sortCondition.Remove(posOfFieldName, 1);  
 }  
 }  
 }  

 // sort the grid  
 this.dataSet11.Tables[0].DefaultView.Sort = sortCondition;  
 this.Text = "C1TrueDbGrid Sort Condition: " + (sortCondition!=""?sortCondition: "None");  
 }  
 }

An image showing the MultiColumn sorting functionality of the TrueDBGrid control

Hunter Haaf