Skip to content

Instantly share code, notes, and snippets.

@sayurin
Created September 10, 2014 14:11
Show Gist options
  • Save sayurin/b9e73098a9818f38a358 to your computer and use it in GitHub Desktop.
Save sayurin/b9e73098a9818f38a358 to your computer and use it in GitHub Desktop.
Automatically sort library for WPF ListView (GridView).
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
namespace Sayuri.Windows {
public static class GridViewSort {
class SortIcon : Adorner {
static readonly Geometry ascending = Geometry.Parse("M 0 4 L 3.5 0 L 7 4 Z");
static readonly Geometry descending = Geometry.Parse("M 0 0 L 3.5 4 L 7 0 Z");
readonly ListSortDirection direction;
public SortIcon(UIElement addornedElement, ListSortDirection direction) : base(addornedElement) {
this.direction = direction;
}
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
if (20 <= AdornedElement.RenderSize.Width) {
drawingContext.PushTransform(new TranslateTransform(AdornedElement.RenderSize.Width - 15, (AdornedElement.RenderSize.Height - 5) / 2));
drawingContext.DrawGeometry(Brushes.Black, null, direction == ListSortDirection.Ascending ? ascending : descending);
drawingContext.Pop();
}
}
}
static void ApplySort(ListView listView, GridViewColumnHeader columnHeader, string propertyName) {
var adorner = GetSortIcon(listView);
if (adorner != null)
AdornerLayer.GetAdornerLayer(adorner.AdornedElement).Remove(adorner);
var items = listView.Items;
var direction = ListSortDirection.Ascending;
if (0 < items.SortDescriptions.Count) {
var current = items.SortDescriptions[0];
if (current.PropertyName == propertyName && current.Direction == ListSortDirection.Ascending)
direction = ListSortDirection.Descending;
}
if (string.IsNullOrEmpty(propertyName))
SetSortIcon(listView, null);
else {
var sortIcon = new SortIcon(columnHeader, direction);
AdornerLayer.GetAdornerLayer(columnHeader).Add(sortIcon);
SetSortIcon(listView, sortIcon);
var description = new SortDescription(propertyName, direction);
if (items.SortDescriptions.Count == 0)
items.SortDescriptions.Add(description);
else
items.SortDescriptions[0] = description;
}
}
static T GetParent<T>(DependencyObject reference) where T : DependencyObject {
var parent = VisualTreeHelper.GetParent(reference);
return parent == null ? null : parent as T ?? GetParent<T>(parent);
}
static void ColumnHeaderClicked(object sender, RoutedEventArgs e) {
var clickedHeader = (GridViewColumnHeader)e.OriginalSource;
var clickedColumn = clickedHeader.Column;
if (clickedColumn == null)
return;
var propertyName = GetMemberPath(clickedHeader.Column);
if (string.IsNullOrEmpty(propertyName)) {
var binding = clickedHeader.Column.DisplayMemberBinding as Binding;
if (binding != null && binding.Path != null)
propertyName = binding.Path.Path;
}
if (!string.IsNullOrEmpty(propertyName)) {
var listView = GetParent<ListView>(clickedHeader);
if (listView != null)
ApplySort(listView, clickedHeader, propertyName);
}
}
public static readonly DependencyProperty SortIconProperty = DependencyProperty.RegisterAttached("SortIcon", typeof(Adorner), typeof(GridViewSort));
private static Adorner GetSortIcon(ListView target) {
return (Adorner)target.GetValue(SortIconProperty);
}
public static void SetSortIcon(ListView target, Adorner value) {
target.SetValue(SortIconProperty, value);
}
public static readonly DependencyProperty MemberPathProperty = DependencyProperty.RegisterAttached("MemberPath", typeof(string), typeof(GridViewSort));
public static string GetMemberPath(GridViewColumn target) {
return (string)target.GetValue(MemberPathProperty);
}
public static void SetSortIcon(GridViewColumn target, string value) {
target.SetValue(MemberPathProperty, value);
}
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(GridViewSort), new UIPropertyMetadata(false, (o, e) => {
var listView = (ListView)o;
var oldValue = (bool)e.OldValue;
var newValue = (bool)e.NewValue;
if (oldValue == true && newValue == false)
listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeaderClicked));
else if (oldValue == false && newValue == true)
listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeaderClicked));
}));
public static bool GetIsEnabled(ListView target) {
return (bool)target.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(ListView target, bool value) {
target.SetValue(IsEnabledProperty, value);
}
}
}
namespace Sayuri.Windows
open System
open System.ComponentModel
open System.Windows
open System.Windows.Controls
open System.Windows.Data
open System.Windows.Documents
open System.Windows.Media
type GridViewSort () =
static let ascending = Geometry.Parse "M 0 4 L 3.5 0 L 7 4 Z"
static let descending = Geometry.Parse "M 0 0 L 3.5 4 L 7 0 Z"
static let applySort (listView : ListView) (columnHeader : GridViewColumnHeader) propertyName =
let adorner : Adorner = GridViewSort.GetSortIcon listView
if adorner <> null then
(AdornerLayer.GetAdornerLayer adorner.AdornedElement).Remove adorner
let items = listView.Items
let direction, geometry, insert =
if items.SortDescriptions.Count = 0 then ListSortDirection.Ascending, ascending, true
elif (let current = items.SortDescriptions.[0] in
current.PropertyName <> propertyName || current.Direction = ListSortDirection.Descending) then ListSortDirection.Ascending, ascending, false
else ListSortDirection.Descending, descending, false
if String.IsNullOrEmpty propertyName then
GridViewSort.SetSortIcon(listView, null)
else
let sortIcon = { new Adorner(columnHeader) with
override __.OnRender (drawingContext) =
base.OnRender drawingContext
if columnHeader.RenderSize.Width < 20.0 then () else
drawingContext.PushTransform <| TranslateTransform(columnHeader.RenderSize.Width - 15.0, (columnHeader.RenderSize.Height - 5.0) / 2.0)
drawingContext.DrawGeometry(Brushes.Black, null, geometry)
drawingContext.Pop() }
(AdornerLayer.GetAdornerLayer columnHeader).Add sortIcon
GridViewSort.SetSortIcon(listView, sortIcon)
let description = SortDescription(propertyName, direction)
if insert then items.SortDescriptions.Add description
else items.SortDescriptions.[0] <- description
static let columnHeaderClick = RoutedEventHandler(fun _ e ->
let clickedHeader = e.OriginalSource :?> GridViewColumnHeader
let clickedColumn = clickedHeader.Column
if clickedColumn = null then () else
let propertyName = GridViewSort.GetMemberPath clickedColumn
let propertyName = if String.IsNullOrEmpty propertyName |> not then propertyName else
match clickedColumn.DisplayMemberBinding with
| :? Binding as binding when binding.Path <> null -> binding.Path.Path
| _ -> null
if String.IsNullOrEmpty propertyName |> not then
let rec loop reference =
match VisualTreeHelper.GetParent reference with
| :? ListView as listView -> Some listView
| null -> None
| parent -> loop parent
loop clickedHeader |> Option.iter (fun listView -> applySort listView clickedHeader propertyName))
static member val SortIconProperty = DependencyProperty.RegisterAttached("SortIcon", typeof<Adorner>, typeof<GridViewSort>)
static member private GetSortIcon (target : ListView) =
target.GetValue GridViewSort.SortIconProperty :?> Adorner
static member SetSortIcon (target : ListView, value : Adorner) =
target.SetValue(GridViewSort.SortIconProperty, value)
static member val MemberPathProperty = DependencyProperty.RegisterAttached("MemberPath", typeof<string>, typeof<GridViewSort>)
static member GetMemberPath (target : GridViewColumn) =
target.GetValue GridViewSort.MemberPathProperty :?> string
static member SetMemberPath (target : GridViewColumn, value : string) =
target.SetValue(GridViewSort.MemberPathProperty, value)
static member val IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof<bool>, typeof<GridViewSort>, UIPropertyMetadata(false, fun o e ->
let listView = o :?> ListView
match downcast e.OldValue, downcast e.NewValue with
| true, false -> listView.RemoveHandler(GridViewColumnHeader.ClickEvent, columnHeaderClick)
| false, true -> listView.AddHandler(GridViewColumnHeader.ClickEvent, columnHeaderClick)
| _, _ -> ()))
static member GetIsEnabled (target : ListView) =
target.GetValue GridViewSort.IsEnabledProperty :?> bool
static member SetIsEnabled (target : ListView, value : bool) =
target.SetValue(GridViewSort.IsEnabledProperty, value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment