Well this is the first post of 2013 and has been brewing for a while, what we want is to be able to set the initial sort of the GridView on the List page or ListDetails page. Listing 1 shows what we want to achieve.
Figure 1 – OrderDate sorted Descending and ShipName sorted Ascending
The Multi Column Sort Attribute
Figure 2 shows the first version of the attribute, I was using magic strings. But in Figure 3 we have an enum from System.Web.UI.WebControls to help us out (I’m a bad speller and I tend to type too quickly) so I think that is better.
Figure 2 – Early Version Of Metadata
Figure 3 – Example Metadata
Listing 1 shows the MultiColumnSortAttribute I am using the params construct to get a list of values from the attribute and I am using the BuildColumnParametersDictionary method to convert the object array to a dictionary with a string key the column name and a value of the SortDirection.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MultiColumnSortAttribute : Attribute
{
private IDictionary<String, SortDirection> _columns;
public IDictionary<String, SortDirection> Columns
{
get { return this._columns; }
}
public MultiColumnSortAttribute(params object[] columnParameters)
{
_columns = BuildColumnParametersDictionary(columnParameters);
}
private IDictionary<String, SortDirection> BuildColumnParametersDictionary(object[] objArray)
{
var dictionary = new Dictionary<String, SortDirection>();
if ((objArray != null) && (objArray.Length != 0))
{
if ((objArray.Length % 2) != 0)
throw new InvalidOperationException(
String.Format("Need even number of control parameters.",
new object[0]));
for (int i = 0; i < objArray.Length; i += 2)
{
if (objArray[i] is String && objArray[i + 1] is SortDirection)
{
if (objArray[i] == null)
throw new InvalidOperationException(
String.Format("Column name [{0}] is null.", i));
// get column name from array
String columnName = objArray[i] as String;
if (columnName == null)
throw new InvalidOperationException(
String.Format("Column name '{0}' is not a String.",
objArray[i].ToString()));
// check for duplicate column names
if (dictionary.ContainsKey(columnName))
throw new InvalidOperationException(
String.Format("Column name '{0}' occurs more than once.",
columnName));
// get sort direction from array
if (objArray[i + 1] == null)
throw new InvalidOperationException(
String.Format("Sort direction '{0}' is not a of type SortDirection.",
objArray[i].ToString()));
SortDirection sortDirection = (SortDirection)objArray[i + 1];
dictionary[columnName] = sortDirection;
}
}
}
return dictionary;
}
}
Listing 1 - MultiColumnSortAttribute
An Extension Method to Add Multi Column Sort to the Page
This extension method in Listing 2 is a little more complicated than it would need to be to handle just the Multi Column Sort, it also handles the DisplayColumn attribute to, this way we cover all bases.
/// <summary>
/// Sets the initial sort order.
/// </summary>
/// <param name="queryExtender">The query extender.</param>
/// <param name="table">The table.</param>
public static void SetInitialSortOrder(this QueryExtender queryExtender, MetaTable table)
{
var multiColumnSort = table.GetAttribute<MultiColumnSortAttribute>();
if (multiColumnSort != null)
{
var firstEntry = multiColumnSort.Columns.Keys.First();
var order = new OrderByExpression()
{
DataField = firstEntry,
Direction = multiColumnSort.Columns[firstEntry],
};
foreach (var item in multiColumnSort.Columns)
{
if (item.Key != firstEntry)
{
order.ThenByExpressions.Add(new ThenBy()
{
DataField = item.Key,
Direction = item.Value
});
}
}
queryExtender.Expressions.Add(order);
}
else if (table.SortColumn != null)
{
var order = new OrderByExpression()
{
DataField = table.SortColumn.Name,
Direction = table.SortDescending ? SortDirection.Descending : SortDirection.Ascending,
};
queryExtender.Expressions.Add(order);
}
}
Listing 2 – SetInitialSortOrder Extension Method
Using in the List Page Template
All we need to do in the List and ListDetails page templates is to add one line to the Page_Init method.
protected void Page_Init(object sender, EventArgs e)
{
table = DynamicDataRouteHandler.GetRequestMetaTable(Context);
GridView1.SetMetaTable(table, table.GetColumnValuesFromRoute(Context));
GridDataSource.EntityTypeFilter = table.EntityType.Name;
// set initial sort order
GridQueryExtender.SetInitialSortOrder(table);
}
Listing 3 – adding the extension method to the Page Template
Download
You may need to look through the list of files on my sky drive to find the one you want, it’s called MultiColumnSort.zip
4 comments:
Hi,
have you try this with mysql datasource?
e can't filter with any Entity Key! :(
Got thi:
"Unable to cast object of type 'System.Web.DynamicData.MetaColumn' to type 'System.Web.DynamicData.MetaForeignKeyColumn'."
What are i doing wrong? In ModelMetadata, have this:
[MetadataTypeAttribute(typeof(trabalho.TrabalhoMetadata))]
[DisplayColumn("ID_Trabalhos", "ID_Trabalhos", true)]
[DisplayName("Trabalhos")]
public partial class trabalho
{
internal sealed class TrabalhoMetadata
{
[Display(Name = "ID", Order = 1)]
[FilterUIHint("ForeignKey")]
public int ID_Trabalhos { get; set; }
[FilterUIHint("Contains")]
[Display(Name = "Trabalho", Order = 2)]
public string NomeTrab { get; set; }
maybe it's simple, but could u help
Luís Faria
As far as i know this only works with Entity Framework and Linq to SQL, you can however get an entity framework connector for MySql
Steve
Hi, if I want to sort by a foreign key column, how would I reference that? For example, lets say I have an Orders table, but I want to sort by Customer Name, which is in a Customers table and related to Orders by Orders.CustID? In an example like this, what would my MultiColumnSort attribute need to be or is that not accounted for in the Attribute? Thanks for all your helpful articles!
Hi jh, yes you would use the "Orders.Customer.CustomerName" I think this will work.
Steve.
Post a Comment