/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; namespace dnSpy.Contracts.MVVM { /// /// Adds column sorting code to a . The VM gets notified when a column is clicked /// and it sorts its list. /// public sealed class GridViewColumnSorter { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public static readonly DependencyProperty ColumnProviderProperty = DependencyProperty.RegisterAttached( "ColumnProvider", typeof(IGridViewColumnDescsProvider), typeof(GridViewColumnSorter), new UIPropertyMetadata(null, ColumnProviderPropertyChangedCallback)); public static void SetColumnProvider(FrameworkElement element, IGridViewColumnDescsProvider value) => element.SetValue(ColumnProviderProperty, value); public static IGridViewColumnDescsProvider GetColumnProvider(FrameworkElement element) => (IGridViewColumnDescsProvider)element.GetValue(ColumnProviderProperty); public static readonly DependencyProperty GridViewSortDirectionProperty = DependencyProperty.RegisterAttached( "GridViewSortDirection", typeof(GridViewSortDirection), typeof(GridViewColumnSorter), new UIPropertyMetadata(GridViewSortDirection.Default)); public static void SetGridViewSortDirection(GridViewColumn element, GridViewSortDirection value) => element.SetValue(GridViewSortDirectionProperty, value); public static GridViewSortDirection GetGridViewSortDirection(GridViewColumn element) => (GridViewSortDirection)element.GetValue(GridViewSortDirectionProperty); public static readonly DependencyProperty IdProperty = DependencyProperty.RegisterAttached( "Id", typeof(int), typeof(GridViewColumnSorter), new UIPropertyMetadata(int.MinValue)); public static void SetId(GridViewColumn element, int value) => element.SetValue(IdProperty, value); public static int GetId(GridViewColumn element) => (int)element.GetValue(IdProperty); #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member static readonly DependencyProperty GridViewColumnSorterInstanceProperty = DependencyProperty.RegisterAttached( "GridViewColumnSorterInstance", typeof(GridViewColumnSorter), typeof(GridViewColumnSorter), new UIPropertyMetadata(null)); readonly ListView listView; readonly Dictionary toDesc; IGridViewColumnDescsProvider? descsProvider; GridViewColumnSorter(ListView listView) { this.listView = listView; toDesc = new Dictionary(); } static GridViewColumnSorter GetInstance(ListView listView) { var inst = (GridViewColumnSorter)listView.GetValue(GridViewColumnSorterInstanceProperty); if (inst is null) { inst = new GridViewColumnSorter(listView); listView.SetValue(GridViewColumnSorterInstanceProperty, inst); } return inst; } static void ColumnProviderPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { var sorter = GetInstance((ListView)d); sorter.Initialize((IGridViewColumnDescsProvider)e.NewValue); } void Initialize(IGridViewColumnDescsProvider descsProvider) { if (descsProvider is null) return; Debug2.Assert(this.descsProvider is null); this.descsProvider = descsProvider; descsProvider.Descs.SortedColumnChanged += OnSortedColumnChanged; listView.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ListView_Click)); var gridView = (GridView)listView.View; gridView.Columns.CollectionChanged += GridView_Columns_CollectionChanged; var cols = gridView.Columns; var idToDesc = new Dictionary(); foreach (var desc in descsProvider.Descs.Columns) idToDesc.Add(desc.Id, desc); foreach (var col in cols) { int colId = GetId(col); if (!idToDesc.TryGetValue(colId, out var desc)) throw new InvalidOperationException("Missing GridViewColumn Id"); toDesc.Add(col, desc); col.Header = desc.Name; } UpdateColumns(); } void GridView_Columns_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { Debug2.Assert(descsProvider is not null); var gridView = (GridView)listView.View; var columns = gridView.Columns; Debug.Assert(columns.Count == descsProvider.Descs.Columns.Length); descsProvider.Descs.Columns = columns.Select(a => toDesc[a]).ToArray(); } void OnSortedColumnChanged(object? sender, EventArgs e) { Debug.Assert(descsProvider?.Descs == sender); UpdateColumns(); } void ListView_Click(object? sender, RoutedEventArgs e) { var column = (e.OriginalSource as GridViewColumnHeader)?.Column; if (column is null || !toDesc.TryGetValue(column, out var desc)) return; e.Handled = true; UpdateSortedColumn(desc); } void UpdateSortedColumn(GridViewColumnDesc desc) { Debug2.Assert(descsProvider is not null); if (!desc.CanBeSorted) return; var sortedColumn = descsProvider.Descs.SortedColumn; var sortDir = GetGridViewSortDirection(sortedColumn.Column == desc ? sortedColumn.Direction : GridViewSortDirection.Default); descsProvider.Descs.SortedColumn = new GridViewSortedColumn(sortDir == GridViewSortDirection.Default ? null : desc, sortDir); } static GridViewSortDirection GetGridViewSortDirection(GridViewSortDirection direction) { switch (direction) { case GridViewSortDirection.Default: return GridViewSortDirection.Ascending; case GridViewSortDirection.Ascending: return GridViewSortDirection.Descending; case GridViewSortDirection.Descending: return GridViewSortDirection.Default; default: throw new InvalidOperationException(); } } void UpdateColumns() { Debug2.Assert(descsProvider is not null); var gridView = (GridView)listView.View; var cols = gridView.Columns; var sortedColumn = descsProvider.Descs.SortedColumn; foreach (var col in cols) { var desc = toDesc[col]; var dir = sortedColumn.Column == desc ? sortedColumn.Direction : GridViewSortDirection.Default; SetGridViewSortDirection(col, dir); } } } }