I had a need to set footer rows what will not be invoked in the sorting. There I have a summary information base on all previous rows. Here are just some sample data.
Not sorted
Sorted Ascending
Sorted descending
Sorted by Country property (Ascending)
I used Datagrid from WpfToolkit.
To achive this goal I did next:
- Created custom Datagrid inherited from the standard one. Overrided Sorting method.
- Created custom Collection. It is based on List<T> and uses custom comparer.
- Used PropertyAccessor to get values by property name. Of course you can use reflection but it's not fast as you know. This one is quite fast. So, why not?
1. Creating custom DataGrid.
I have aded one more property called FooterRowsCount and overrided sorting method. Property indicates how many of last rows should not be involved in sorting. and should be always be in the end of items.Here is the code.
using System.ComponentModel;
using System.Windows;
using Microsoft.Windows.Controls;
namespace delWPF
{
public class FooterDataGrid : DataGrid
{
public static readonly DependencyProperty FooterRowsCountProperty =
DependencyProperty.Register("FooterRowsCount", typeof(int), typeof(FooterDataGrid),
new PropertyMetadata(default(int)));
public int FooterRowsCount
{
get { return (int)GetValue(FooterRowsCountProperty); }
set { SetValue(FooterRowsCountProperty, value); }
}
protected override void OnSorting(DataGridSortingEventArgs e)
{
//if collection doesn't support footers - use basic sorting
var lastRowList = ItemsSource as IFooterList;
if (lastRowList == null)
{
base.OnSorting(e);
return;
}
var column = e.Column;
e.Handled = true;
var direction = (column.SortDirection != ListSortDirection.Ascending)
? ListSortDirection.Ascending
: ListSortDirection.Descending;
column.SortDirection = direction;
//main magic
lastRowList.SortButLastRows(FooterRowsCount, e.Column.SortMemberPath, direction);
Items.Refresh();
}
}
}
2. Creating custom collection
I have created an interface called IFooterList and a class what implements it.using System.Collections;
using System.ComponentModel;
namespace delWPF
{
public interface IFooterList : IEnumerable
{
void SortButLastRows(int footerRowsCount, string propertyName, ListSortDirection direction);
}
}
Standard List<T> class has sort method and you can use it however you want. I used next code. It's not perfect, just first code that works :)
using System.Collections.Generic;
using System.ComponentModel;
namespace delWPF
{
public class FooterList<T> : List<T>, IFooterList
{
public FooterList()
{
}
public FooterList(int capacity)
: base(capacity)
{
}
public FooterList(IEnumerable<T> collection)
: base(collection)
{
}
public void SortButLastRows(int footerRowsCount, string propertyName, ListSortDirection direction)
{
var comparerLast = new PropertyComparer<T>(propertyName, direction);
int totalCount = Count;
int countToSort = totalCount > footerRowsCount ? totalCount - footerRowsCount : 0;
//Tell to sort method not to sort footer rows.
Sort(0, countToSort, comparerLast);
}
}
}
3. Custom comparer for you collection
Also I have used a custom class to compare values by propety name and direction. It is based on 3rd party code. Sorry, but cannot find for now the link to the author's blog. Only modification I made it is using of PropertyAccessor taken from James Nies blog instead of PropertyDescriptor.using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
namespace delWPF
{
public class PropertyComparer<T> : IComparer<T>
{
private readonly IComparer comparer;
private PropertyAccessor accessor;
private int reverse;
public PropertyComparer(string propertyName, ListSortDirection direction)
{
accessor = new PropertyAccessor(typeof(T), propertyName);
var comparerForPropertyType =
typeof(Comparer<>).MakeGenericType(typeof(T).GetProperty(propertyName).PropertyType);
comparer =
(IComparer)
comparerForPropertyType.InvokeMember("Default",
BindingFlags.Static | BindingFlags.GetProperty |
BindingFlags.Public, null, null, null);
SetListSortDirection(direction);
}
#region IComparer<T> Members
public int Compare(T x, T y)
{
return reverse * comparer.Compare(accessor.Get(x), accessor.Get(y));
}
#endregion
private void SetListSortDirection(ListSortDirection direction)
{
reverse = direction == ListSortDirection.Ascending ? 1 : -1;
}
public void SetPropertyAndDirection(string name, ListSortDirection direction)
{
SetListSortDirection(direction);
accessor = new PropertyAccessor(typeof(T), name);
}
}
}
That's all. To use it crean in your designer the FooterDataGrid (or use standard one, but you have to subscribe to Sorting event and implement there logic)
<delWPF:FooterDataGrid x:Name="datagrid" AutoGenerateColumns="True"
FooterRowsCount="2" CanUserAddRows="False" IsReadOnly="True"/>
4. Set style for footers row.
As far as you can see I have set different collors to the grid rows. There are alternation and footer rows have their own background. public class ListViewItemStyleSelector : StyleSelector
{
public override Style SelectStyle(object item,
DependencyObject container)
{
var st = new Style {TargetType = typeof (DataGridRow)};
var backGroundSetter = new Setter {Property = Control.BackgroundProperty};
var grid = (FooterDataGrid) ItemsControl.ItemsControlFromItemContainer(container);
int index = grid.ItemContainerGenerator.IndexFromContainer(container);
bool isFooter = IsFooterRow(grid, index);
if (isFooter)
{
backGroundSetter.Value = Brushes.Orange;
}
else if (index%2 == 0)
{
backGroundSetter.Value = Brushes.LightBlue;
}
else
{
backGroundSetter.Value = Brushes.White;
}
st.Setters.Add(backGroundSetter);
return st;
}
private bool IsFooterRow(FooterDataGrid grid, int index)
{
return index >= grid.Items.Count - grid.FooterRowsCount;
}
}
The result you can see in the top of the article. Of course here is better to use styles from dictionary, but it's another story.Download sample project
Happy codding!








0 коммент.:
Отправить комментарий