mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-27 04:09:37 +08:00
Add DataGridSortDescription
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
namespace AtomUI.Controls.Theme;
|
||||
|
||||
internal class DataGridTheme
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using AtomUI.Controls.XDataGrid.Utils;
|
||||
using Avalonia.Collections;
|
||||
|
||||
namespace AtomUI.Controls.XDataGrid.Data;
|
||||
|
||||
public abstract class DataGridSortDescription
|
||||
{
|
||||
public virtual string? PropertyPath => null;
|
||||
public virtual ListSortDirection Direction => ListSortDirection.Ascending;
|
||||
public bool HasPropertyPath => !string.IsNullOrEmpty(PropertyPath);
|
||||
public abstract IComparer<object> Comparer { get; }
|
||||
|
||||
public virtual IOrderedEnumerable<object> OrderBy(IEnumerable<object> seq)
|
||||
{
|
||||
return seq.OrderBy(o => o, Comparer);
|
||||
}
|
||||
|
||||
public virtual IOrderedEnumerable<object> ThenBy(IOrderedEnumerable<object> seq)
|
||||
{
|
||||
return seq.ThenBy(o => o, Comparer);
|
||||
}
|
||||
|
||||
public virtual DataGridSortDescription SwitchSortDirection()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
internal virtual void Initialize(Type itemType)
|
||||
{
|
||||
}
|
||||
|
||||
private static object? InvokePath(object item, string propertyPath, Type propertyType)
|
||||
{
|
||||
object? propertyValue =
|
||||
TypeHelper.GetNestedPropertyValue(item, propertyPath, propertyType, out Exception? exception);
|
||||
if (exception != null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
public static DataGridSortDescription FromPath(string propertyPath, ListSortDirection direction = ListSortDirection.Ascending, CultureInfo? culture = null)
|
||||
{
|
||||
return new DataGridPathSortDescription(propertyPath, direction, null, culture);
|
||||
}
|
||||
|
||||
public static DataGridSortDescription FromPath(string propertyPath, ListSortDirection direction, IComparer comparer)
|
||||
{
|
||||
return new DataGridPathSortDescription(propertyPath, direction, comparer, null);
|
||||
}
|
||||
|
||||
public static DataGridSortDescription FromComparer(IComparer comparer, ListSortDirection direction = ListSortDirection.Ascending)
|
||||
{
|
||||
return new DataGridComparerSortDescription(comparer, direction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a comparer class that takes in a CultureInfo as a parameter,
|
||||
/// which it will use when comparing strings.
|
||||
/// </summary>
|
||||
private class CultureSensitiveComparer : Comparer<object>
|
||||
{
|
||||
/// <summary>
|
||||
/// Private accessor for the CultureInfo of our comparer
|
||||
/// </summary>
|
||||
private CultureInfo _culture;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a comparer which will respect the CultureInfo
|
||||
/// that is passed in when comparing strings.
|
||||
/// </summary>
|
||||
/// <param name="culture">The CultureInfo to use in string comparisons</param>
|
||||
public CultureSensitiveComparer(CultureInfo? culture)
|
||||
{
|
||||
_culture = culture ?? CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two objects and returns a value indicating whether one is less than, equal to or greater than the other.
|
||||
/// </summary>
|
||||
/// <param name="x">first item to compare</param>
|
||||
/// <param name="y">second item to compare</param>
|
||||
/// <returns>Negative number if x is less than y, zero if equal, and a positive number if x is greater than y</returns>
|
||||
/// <remarks>
|
||||
/// Compares the 2 items using the specified CultureInfo for string and using the default object comparer for all other objects.
|
||||
/// </remarks>
|
||||
public override int Compare(object? x, object? y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
if (y != null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// at this point x and y are not null
|
||||
if (x is string stringX && y is string stringY)
|
||||
{
|
||||
return _culture.CompareInfo.Compare(stringX, stringY);
|
||||
}
|
||||
|
||||
return Default.Compare(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private class DataGridPathSortDescription : DataGridSortDescription
|
||||
{
|
||||
private readonly ListSortDirection _direction;
|
||||
private readonly string _propertyPath;
|
||||
private readonly Lazy<CultureSensitiveComparer> _cultureSensitiveComparer;
|
||||
private readonly Lazy<IComparer<object>> _comparer;
|
||||
private Type? _propertyType;
|
||||
private IComparer? _internalComparer;
|
||||
private IComparer<object?>? _internalComparerTyped;
|
||||
|
||||
private IComparer<object?>? InternalComparer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_internalComparerTyped == null && _internalComparer != null)
|
||||
{
|
||||
if (_internalComparer is IComparer<object?> c)
|
||||
{
|
||||
_internalComparerTyped = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
_internalComparerTyped = Comparer<object?>.Create((x, y) => _internalComparer.Compare(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return _internalComparerTyped;
|
||||
}
|
||||
}
|
||||
|
||||
public override string PropertyPath => _propertyPath;
|
||||
public override IComparer<object> Comparer => _comparer.Value;
|
||||
public override ListSortDirection Direction => _direction;
|
||||
|
||||
public DataGridPathSortDescription(string propertyPath, ListSortDirection direction, IComparer? internalComparer,
|
||||
CultureInfo? culture)
|
||||
{
|
||||
_propertyPath = propertyPath;
|
||||
_direction = direction;
|
||||
_cultureSensitiveComparer = new Lazy<CultureSensitiveComparer>(() =>
|
||||
new CultureSensitiveComparer(culture ?? CultureInfo.CurrentCulture));
|
||||
_internalComparer = internalComparer;
|
||||
_comparer = new Lazy<IComparer<object>>(() => Comparer<object>.Create((x, y) => Compare(x, y)));
|
||||
}
|
||||
|
||||
private DataGridPathSortDescription(DataGridPathSortDescription inner, ListSortDirection direction)
|
||||
{
|
||||
_propertyPath = inner._propertyPath;
|
||||
_direction = direction;
|
||||
_propertyType = inner._propertyType;
|
||||
_cultureSensitiveComparer = inner._cultureSensitiveComparer;
|
||||
_internalComparer = inner._internalComparer;
|
||||
_internalComparerTyped = inner._internalComparerTyped;
|
||||
|
||||
_comparer = new Lazy<IComparer<object>>(() => Comparer<object>.Create((x, y) => Compare(x, y)));
|
||||
}
|
||||
|
||||
private object? GetValue(object? o)
|
||||
{
|
||||
if (o == null)
|
||||
return null;
|
||||
|
||||
if (HasPropertyPath)
|
||||
{
|
||||
return InvokePath(o, _propertyPath, _propertyType!);
|
||||
}
|
||||
|
||||
if (_propertyType == o.GetType())
|
||||
{
|
||||
return o;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IComparer? GetComparerForType(Type type)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return _cultureSensitiveComparer.Value;
|
||||
}
|
||||
return GetComparerForNotStringType(type);
|
||||
}
|
||||
|
||||
internal static IComparer? GetComparerForNotStringType(Type type)
|
||||
{
|
||||
if (System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported == false)
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) &&
|
||||
type.GetGenericArguments()[0].IsAssignableTo(typeof(IComparable)))
|
||||
{
|
||||
return Comparer<object>.Create((x, y) => (x as IComparable)!.CompareTo(y));
|
||||
}
|
||||
|
||||
if (type.IsAssignableTo(typeof(IComparable)))
|
||||
{
|
||||
//enum should be here
|
||||
return Comparer<object>.Create((x, y) => (x as IComparable)!.CompareTo(y));
|
||||
}
|
||||
return Comparer<object>.Create((x, y) => 0); //avoid using reflection to avoid crash on AOT
|
||||
}
|
||||
return (typeof(Comparer<>).MakeGenericType(type).GetProperty("Default"))?.GetValue(null, null) as
|
||||
IComparer;
|
||||
}
|
||||
|
||||
private Type? GetPropertyType(object o)
|
||||
{
|
||||
return o.GetType().GetNestedPropertyType(_propertyPath);
|
||||
}
|
||||
|
||||
private int Compare(object? x, object? y)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (_propertyType == null)
|
||||
{
|
||||
if (x != null)
|
||||
{
|
||||
_propertyType = GetPropertyType(x);
|
||||
}
|
||||
|
||||
if (_propertyType == null && y != null)
|
||||
{
|
||||
_propertyType = GetPropertyType(y);
|
||||
}
|
||||
}
|
||||
|
||||
var v1 = GetValue(x);
|
||||
var v2 = GetValue(y);
|
||||
|
||||
if (_propertyType != null)
|
||||
{
|
||||
_internalComparer = GetComparerForType(_propertyType);
|
||||
}
|
||||
|
||||
result = _internalComparer?.Compare(v1, v2) ?? 0;
|
||||
|
||||
if (Direction == ListSortDirection.Descending)
|
||||
{
|
||||
return -result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal override void Initialize(Type itemType)
|
||||
{
|
||||
base.Initialize(itemType);
|
||||
|
||||
if (_propertyType == null)
|
||||
{
|
||||
_propertyType = itemType.GetNestedPropertyType(_propertyPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
_internalComparer = GetComparerForType(_propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
public override IOrderedEnumerable<object> OrderBy(IEnumerable<object> seq)
|
||||
{
|
||||
if (Direction == ListSortDirection.Descending)
|
||||
{
|
||||
return seq.OrderByDescending(o => GetValue(o), InternalComparer);
|
||||
}
|
||||
return seq.OrderBy(o => GetValue(o), InternalComparer);
|
||||
}
|
||||
|
||||
public override IOrderedEnumerable<object> ThenBy(IOrderedEnumerable<object> seq)
|
||||
{
|
||||
if (Direction == ListSortDirection.Descending)
|
||||
{
|
||||
return seq.ThenByDescending(o => GetValue(o), InternalComparer);
|
||||
}
|
||||
return seq.ThenBy(o => GetValue(o), InternalComparer);
|
||||
}
|
||||
|
||||
public override DataGridSortDescription SwitchSortDirection()
|
||||
{
|
||||
var newDirection = _direction == ListSortDirection.Ascending
|
||||
? ListSortDirection.Descending
|
||||
: ListSortDirection.Ascending;
|
||||
return new DataGridPathSortDescription(this, newDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DataGridComparerSortDescription : DataGridSortDescription
|
||||
{
|
||||
private readonly IComparer _innerComparer;
|
||||
private readonly ListSortDirection _direction;
|
||||
private readonly IComparer<object> _comparer;
|
||||
|
||||
public IComparer SourceComparer => _innerComparer;
|
||||
public override IComparer<object> Comparer => _comparer;
|
||||
public override ListSortDirection Direction => _direction;
|
||||
public DataGridComparerSortDescription(IComparer comparer, ListSortDirection direction)
|
||||
{
|
||||
_innerComparer = comparer;
|
||||
_direction = direction;
|
||||
_comparer = Comparer<object>.Create((x, y) => Compare(x, y));
|
||||
}
|
||||
|
||||
private int Compare(object x, object y)
|
||||
{
|
||||
int result = _innerComparer.Compare(x, y);
|
||||
|
||||
if (Direction == ListSortDirection.Descending)
|
||||
return -result;
|
||||
else
|
||||
return result;
|
||||
}
|
||||
public override DataGridSortDescription SwitchSortDirection()
|
||||
{
|
||||
var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
|
||||
return new DataGridComparerSortDescription(_innerComparer, newDirection);
|
||||
}
|
||||
}
|
||||
|
||||
public class DataGridSortDescriptionCollection : AvaloniaList<DataGridSortDescription>
|
||||
{ }
|
||||
@@ -0,0 +1,38 @@
|
||||
// (c) Copyright Microsoft Corporation.
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
||||
// All other rights reserved.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
|
||||
namespace AtomUI.Controls.XDataGrid.Data;
|
||||
|
||||
public interface IDataGridCollectionView : IEnumerable, INotifyCollectionChanged
|
||||
{
|
||||
/// <summary>Gets or sets the cultural information for any operations of the view that may differ by culture, such as sorting.</summary>
|
||||
/// <returns>The culture information to use during culture-sensitive operations. </returns>
|
||||
CultureInfo Culture { get; set; }
|
||||
|
||||
/// <summary>Indicates whether the specified item belongs to this collection view. </summary>
|
||||
/// <returns>true if the item belongs to this collection view; otherwise, false.</returns>
|
||||
/// <param name="item">The object to check. </param>
|
||||
bool Contains(object item);
|
||||
|
||||
/// <summary>Gets the underlying collection.</summary>
|
||||
/// <returns>The underlying collection.</returns>
|
||||
IEnumerable SourceCollection { get; }
|
||||
|
||||
/// <summary>Gets or sets a callback that is used to determine whether an item is appropriate for inclusion in the view. </summary>
|
||||
/// <returns>A method that is used to determine whether an item is appropriate for inclusion in the view.</returns>
|
||||
Func<object, bool> Filter { get; set; }
|
||||
|
||||
/// <summary>Gets a value that indicates whether this view supports filtering by way of the <see cref="P:System.ComponentModel.ICollectionView.Filter" /> property.</summary>
|
||||
/// <returns>true if this view supports filtering; otherwise, false.</returns>
|
||||
bool CanFilter { get; }
|
||||
|
||||
/// <summary>Gets a collection of <see cref="T:System.ComponentModel.SortDescription" /> instances that describe how the items in the collection are sorted in the view.</summary>
|
||||
/// <returns>A collection of values that describe how the items in the collection are sorted in the view.</returns>
|
||||
DataGridSortDescriptionCollection SortDescriptions { get; }
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ using AtomUI.Theme.TokenSystem;
|
||||
using Avalonia;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace AtomUI.Controls.Theme;
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
[ControlDesignToken]
|
||||
internal class DataGridToken : AbstractControlDesignToken
|
||||
Reference in New Issue
Block a user