Created
September 10, 2014 14:11
-
-
Save sayurin/b9e73098a9818f38a358 to your computer and use it in GitHub Desktop.
Automatically sort library for WPF ListView (GridView).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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