Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private void Setup()
// right list
AdvancedCollectionView acv = new(EmployeeCollection);
acv.Filter = x => !int.TryParse(((Employee)x).Name, out _);
acv.SortDescriptions.Add(new(nameof(Employee.Name), SortDirection.Ascending));
acv.SortDescriptions.Add(new SortDescription<Employee>(nameof(Employee.Name), SortDirection.Ascending));

CollectionView = acv;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ namespace CommunityToolkit.WinUI.Collections;
/// <summary>
/// A collection view implementation that supports filtering, sorting and incremental loading
/// </summary>
#if NET8_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Item sorting uses reflection to get property types and may not be AOT compatible.")]
#endif
public partial class AdvancedCollectionView : IAdvancedCollectionView, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<object>
{
private readonly List<object> _view;
Expand Down Expand Up @@ -383,7 +380,7 @@ public Predicate<object> Filter
int IComparer<object>.Compare(object x, object y)
#pragma warning restore CA1033 // Interface methods should be callable by child types
{
if (!_sortProperties.Any())
if (_sortProperties.Count == 0)
{
var listType = _source?.GetType();
Type type;
Expand All @@ -401,7 +398,7 @@ int IComparer<object>.Compare(object x, object y)
{
if (!string.IsNullOrEmpty(sd.PropertyName))
{
_sortProperties[sd.PropertyName] = type.GetProperty(sd.PropertyName);
_sortProperties[sd.PropertyName] = sd.GetProperty(type);
}
}
}
Expand All @@ -419,8 +416,8 @@ int IComparer<object>.Compare(object x, object y)
{
var pi = _sortProperties[sd.PropertyName];

cx = pi.GetValue(x!);
cy = pi.GetValue(y!);
cx = pi.GetValue(x);
cy = pi.GetValue(y);
}

var cmp = sd.Comparer.Compare(cx, cy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Diagnostics.CodeAnalysis;

namespace CommunityToolkit.WinUI.Collections;

Expand All @@ -14,7 +15,7 @@ public class SortDescription
/// <summary>
/// Gets the name of property to sort on
/// </summary>
public string PropertyName { get; }
public string? PropertyName { get; }

/// <summary>
/// Gets the direction of sort
Expand All @@ -33,8 +34,10 @@ public class SortDescription
/// <param name="direction">Direction of sort</param>
/// <param name="comparer">Comparer to use. If null, will use default comparer</param>
public SortDescription(SortDirection direction, IComparer? comparer = null)
: this(null!, direction, comparer!)
{
PropertyName = null;
Direction = direction;
Comparer = comparer ?? ObjectComparer.Instance;
}

/// <summary>
Expand All @@ -43,13 +46,23 @@ public SortDescription(SortDirection direction, IComparer? comparer = null)
/// <param name="propertyName">Name of property to sort on</param>
/// <param name="direction">Direction of sort</param>
/// <param name="comparer">Comparer to use. If null, will use default comparer</param>
#if NET8_0_OR_GREATER
[RequiresUnreferencedCode("Item sorting with the property name uses reflection to get the property and is not trim-safe. Either use SortDescription<T> to preserve the required metadata, or use the other constructor without a property name.")]
#endif
public SortDescription(string propertyName, SortDirection direction, IComparer? comparer = null)
{
PropertyName = propertyName;
Direction = direction;
Comparer = comparer ?? ObjectComparer.Instance;
}

#if NET8_0_OR_GREATER
[UnconditionalSuppressMessage("Trimming", "IL2070:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The parameter of method does not have matching annotations.",
Justification = "The path which does reflection is only triggered if the user uses the constructor with RequiresUnreferencedCode, which will inform them of the risk.")]
#endif
internal virtual PropertyInfo? GetProperty(Type type)
=> PropertyName != null ? type.GetProperty(PropertyName) : null;

private class ObjectComparer : IComparer
{
public static readonly IComparer Instance = new ObjectComparer();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Diagnostics.CodeAnalysis;

namespace CommunityToolkit.WinUI.Collections;

/// <summary>
/// A generic version of <see cref="SortDescription"/> which preserves the required metadata for reflection-based sorting.
/// </summary>
/// <typeparam name="T">The type to sort</typeparam>
public sealed class SortDescription<
#if NET8_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
#endif
T> : SortDescription
{
private readonly PropertyInfo _prop;

/// <summary>
/// Initializes a new instance of the <see cref="SortDescription{T}"/> class.
/// </summary>
/// <param name="propertyName">Name of property to sort on</param>
/// <param name="direction">Direction of sort</param>
/// <param name="comparer">Comparer to use. If null, will use default comparer</param>
#if NET8_0_OR_GREATER
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
Justification = "This class preserves metadata and ensures at runtime that the received type is compatible.")]
#endif
public SortDescription(string propertyName, SortDirection direction, IComparer? comparer = null) : base(propertyName, direction, comparer)
{
_prop = typeof(T).GetProperty(propertyName) ?? throw new ArgumentException($"Could not find property {propertyName}");
}

internal override PropertyInfo? GetProperty(Type type) =>
(_prop.DeclaringType is not null && _prop.DeclaringType.IsAssignableFrom(type)) ? _prop : throw new ArgumentException("This instance of SortDescription is not compatible with the desired type");
}
Loading