Displaying and sorting by different values in ListView
consider the following WPF/C# program:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
namespace ListVie开发者_运维技巧wSorting
{
public partial class MainWindow : Window
{
public List<SomeClass> someList;
public List<SomeClass> SomeList
{
get { return someList; }
set { someList = value; }
}
public MainWindow()
{
this.DataContext = this;
someList = new List<SomeClass>();
someList.Add(new SomeClass(123));
someList.Add(new SomeClass(456));
someList.Add(new SomeClass(789));
InitializeComponent();
}
GridViewColumnHeader _lastHeaderClicked = null;
ListSortDirection _lastDirection = ListSortDirection.Ascending;
private void listBox1_Click(object sender, RoutedEventArgs e)
{
someGridView.Columns.ToString();
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
ListSortDirection direction;
if (headerClicked != null)
{
if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
{
if (headerClicked != _lastHeaderClicked)
{
direction = ListSortDirection.Ascending;
}
else
{
if (_lastDirection == ListSortDirection.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
string header1 = headerClicked.Column.Header as string;
string header2 = (string)((Binding)((GridViewColumnHeader)e.OriginalSource).Column.DisplayMemberBinding).Path.Path;
Sort(header2, direction);
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
}
}
private void Sort(string sortBy, ListSortDirection direction)
{
ICollectionView dataView = CollectionViewSource.GetDefaultView(someListView.ItemsSource);
dataView.SortDescriptions.Clear();
SortDescription sd = new SortDescription(sortBy, direction);
dataView.SortDescriptions.Add(sd);
dataView.Refresh();
}
}
public class SomeClass
{
private int someValue;
public int SomeValue
{
get { return someValue; }
set { someValue = value; }
}
public SomeClass(int someValue)
{
this.someValue = someValue;
}
public string StringValue
{
get
{
string str = someValue.ToString();
string returnValue = string.Empty;
foreach (char someChar in str)
{
switch (someChar)
{
case '0': returnValue += "ZERO "; break;
case '1': returnValue += "ONE "; break;
case '2': returnValue += "TWO "; break;
case '3': returnValue += "THREE "; break;
case '4': returnValue += "FOUR "; break;
case '5': returnValue += "FIVE "; break;
case '6': returnValue += "SIX "; break;
case '7': returnValue += "SEVEN "; break;
case '8': returnValue += "EIGHT "; break;
case '9': returnValue += "NINE "; break;
default: returnValue += "UNKNOWN "; break;
}
}
return returnValue;
}
}
}
}
...and the following WPF:
<Window x:Class="ListViewSorting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<ListView
x:Name="someListView"
Height="251"
HorizontalAlignment="Left"
GridViewColumnHeader.Click="listBox1_Click"
Margin="22,29,0,0"
VerticalAlignment="Top"
Width="453"
ItemsSource="{Binding SomeList}">
<ListView.View>
<GridView x:Name="someGridView">
<GridViewColumn Width="50" Header="SomeValue" DisplayMemberBinding="{Binding Path=SomeValue}" />
<GridViewColumn Width="100" Header="StringValue" DisplayMemberBinding="{Binding Path=StringValue}" />
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
</Window>
The code creates a list of objects, each of which is capable of holding a single integer value. The WPF creates a table which binds to this list and displays in two separate columns both the numeric value of the item in the list, and the string value. So, for example, for the integer value 123, the first column will display '123' while the second will display 'ONE TWO THREE'.
Clicking the header for the first column correctly sorts the values in ascending/descending order. Clicking the header for the second column does not sort the values correctly because, for example, the string 'FOUR FIVE SIX' is 'less-than' the string 'ONE TWO THREE'.
I would like the second column to sort correctly, or in other words, for the second column to display the 'StringValue'-string field of the class, but sort by the 'SomeValue'-integer field.
You could actually create some code of your own, if you care for a more declarative solution. That way, you wouldn't have to add event handler code in your window code.
First create a class like the following:
public class GridViewSorting
{
public static ICommand GetCommand(DependencyObject obj) { return (ICommand)obj.GetValue(CommandProperty); }
public static void SetCommand(DependencyObject obj, ICommand value) { obj.SetValue(CommandProperty, value); }
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(GridViewSorting),
new UIPropertyMetadata(
null,
(o, e) =>
{
ItemsControl listView = o as ItemsControl;
if (listView != null)
{
if (!GetAutoSort(listView)) // Don't change click handler if AutoSort enabled
{
if (e.OldValue != null && e.NewValue == null)
{
listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
}
if (e.OldValue == null && e.NewValue != null)
{
listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
}
}
}
}
)
);
public static bool GetAutoSort(DependencyObject obj) { return (bool)obj.GetValue(AutoSortProperty); }
public static void SetAutoSort(DependencyObject obj, bool value) { obj.SetValue(AutoSortProperty, value); }
public static readonly DependencyProperty AutoSortProperty =
DependencyProperty.RegisterAttached(
"AutoSort",
typeof(bool),
typeof(GridViewSorting),
new UIPropertyMetadata(
false,
(o, e) =>
{
ListView listView = o as ListView;
if (listView != null)
{
if (GetCommand(listView) == null) // Don't change click handler if a command is set
{
bool oldValue = (bool)e.OldValue;
bool newValue = (bool)e.NewValue;
if (oldValue && !newValue)
{
listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
}
if (!oldValue && newValue)
{
listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
}
}
}
}
)
);
public static string GetPropertyName(DependencyObject obj) { return (string)obj.GetValue(PropertyNameProperty); }
public static void SetPropertyName(DependencyObject obj, string value) { obj.SetValue(PropertyNameProperty, value); }
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.RegisterAttached(
"PropertyName",
typeof(string),
typeof(GridViewSort),
new UIPropertyMetadata(null)
);
private static void ColumnHeader_Click(object sender, RoutedEventArgs e)
{
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
if (headerClicked != null)
{
string propertyName = GetPropertyName(headerClicked.Column);
if (!string.IsNullOrEmpty(propertyName))
{
ListView listView = GetAncestor<ListView>(headerClicked);
if (listView != null)
{
ICommand command = GetCommand(listView);
if (command != null)
{
if (command.CanExecute(propertyName))
{
command.Execute(propertyName);
}
}
else if (GetAutoSort(listView))
{
ApplySort(listView.Items, propertyName);
}
}
}
}
}
public static T GetAncestor<T>(DependencyObject reference) where T : DependencyObject
{
DependencyObject parent = VisualTreeHelper.GetParent(reference);
while (!(parent is T))
{
parent = VisualTreeHelper.GetParent(parent);
}
if (parent != null)
return (T)parent;
return null;
}
public static void ApplySort(ICollectionView view, string propertyName)
{
ListSortDirection direction = ListSortDirection.Ascending;
if (view.SortDescriptions.Count > 0)
{
SortDescription currentSort = view.SortDescriptions[0];
if (currentSort.PropertyName == propertyName)
{
if (currentSort.Direction == ListSortDirection.Ascending)
direction = ListSortDirection.Descending;
else
direction = ListSortDirection.Ascending;
}
view.SortDescriptions.Clear();
}
if (!string.IsNullOrEmpty(propertyName))
{
view.SortDescriptions.Add(new SortDescription(propertyName, direction));
}
}
}
After that, reference the namespace this class resides in from your top-level xaml node:
xmlns:util="clr-namespace:MyApp.Util"
Finally, you can use this class to set sortkeys declaratively:
<GridViewColumn Header="StringRepresentation"
x:Name="valueColumn"
util:GridViewSort.PropertyName="NumericRepresentation">
I think I found this solution on StackOverflow about a year ago, sorry for not remembering the original proposer...
You will get your result by simply doing this:
private void Sort(string sortBy, ListSortDirection direction)
{
ICollectionView dataView = CollectionViewSource.GetDefaultView(someListView.ItemsSource);
dataView.SortDescriptions.Clear();
SortDescription sd = new SortDescription("SomeValue", direction);
dataView.SortDescriptions.Add(sd);
dataView.Refresh();
}
Simply just put SortDescription first parameter "SomeValue" static.
精彩评论