Skip to content

Commit 84a81da

Browse files
authored
Merge pull request #882 from twsouthwick/acv_comparer
Allow AdvancedCollectionView to sort on object itself and custom comparers
2 parents ffc8a92 + c1e04a0 commit 84a81da

File tree

4 files changed

+242
-19
lines changed

4 files changed

+242
-19
lines changed

Microsoft.Toolkit.Uwp.UI/AdvancedCollectionView/AdvancedCollectionView.cs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -368,27 +368,35 @@ int IComparer<object>.Compare(object x, object y)
368368
var typeInfo = x.GetType().GetTypeInfo();
369369
foreach (var sd in _sortDescriptions)
370370
{
371-
_sortProperties[sd.PropertyName] = typeInfo.GetDeclaredProperty(sd.PropertyName);
371+
if (!string.IsNullOrEmpty(sd.PropertyName))
372+
{
373+
_sortProperties[sd.PropertyName] = typeInfo.GetDeclaredProperty(sd.PropertyName);
374+
}
372375
}
373376
}
374377

375378
foreach (var sd in _sortDescriptions)
376379
{
377-
var pi = _sortProperties[sd.PropertyName];
378-
var cx = pi.GetValue(x) as IComparable;
379-
var cy = pi.GetValue(y) as IComparable;
380-
try
380+
object cx, cy;
381+
382+
if (string.IsNullOrEmpty(sd.PropertyName))
381383
{
382-
// ReSharper disable once PossibleUnintendedReferenceComparison
383-
var cmp = cx == cy ? 0 : cx == null ? -1 : cy == null ? +1 : cx.CompareTo(cy);
384-
if (cmp != 0)
385-
{
386-
return sd.Direction == SortDirection.Ascending ? +cmp : -cmp;
387-
}
384+
cx = x;
385+
cy = y;
388386
}
389-
catch
387+
else
388+
{
389+
var pi = _sortProperties[sd.PropertyName];
390+
391+
cx = pi.GetValue(x);
392+
cy = pi.GetValue(y);
393+
}
394+
395+
var cmp = sd.Comparer.Compare(cx, cy);
396+
397+
if (cmp != 0)
390398
{
391-
// fail silently
399+
return sd.Direction == SortDirection.Ascending ? +cmp : -cmp;
392400
}
393401
}
394402

Microsoft.Toolkit.Uwp.UI/AdvancedCollectionView/SortDescription.cs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
1111
// ******************************************************************
1212

13+
using System;
14+
using System.Collections;
15+
1316
namespace Microsoft.Toolkit.Uwp.UI
1417
{
1518
/// <summary>
@@ -20,22 +23,58 @@ public class SortDescription
2023
/// <summary>
2124
/// Gets the name of property to sort on
2225
/// </summary>
23-
public string PropertyName { get; private set; }
26+
public string PropertyName { get; }
2427

2528
/// <summary>
2629
/// Gets the direction of sort
2730
/// </summary>
28-
public SortDirection Direction { get; private set; }
31+
public SortDirection Direction { get; }
32+
33+
/// <summary>
34+
/// Gets the comparer
35+
/// </summary>
36+
public IComparer Comparer { get; }
37+
38+
/// <summary>
39+
/// Initializes a new instance of the <see cref="SortDescription"/> class that describes
40+
/// a sort on the object itself
41+
/// </summary>
42+
/// <param name="direction">Direction of sort</param>
43+
/// <param name="comparer">Comparer to use. If null, will use default comparer</param>
44+
public SortDescription(SortDirection direction, IComparer comparer = null)
45+
: this(null, direction, comparer)
46+
{
47+
}
2948

3049
/// <summary>
3150
/// Initializes a new instance of the <see cref="SortDescription"/> class.
3251
/// </summary>
33-
/// <param name="propertyName">name of property to sort on</param>
34-
/// <param name="direction">direction of sort</param>
35-
public SortDescription(string propertyName, SortDirection direction)
52+
/// <param name="propertyName">Name of property to sort on</param>
53+
/// <param name="direction">Direction of sort</param>
54+
/// <param name="comparer">Comparer to use. If null, will use default comparer</param>
55+
public SortDescription(string propertyName, SortDirection direction, IComparer comparer = null)
3656
{
3757
PropertyName = propertyName;
3858
Direction = direction;
59+
Comparer = comparer ?? ObjectComparer.Instance;
60+
}
61+
62+
private class ObjectComparer : IComparer
63+
{
64+
public static readonly IComparer Instance = new ObjectComparer();
65+
66+
private ObjectComparer()
67+
{
68+
}
69+
70+
public int Compare(object x, object y)
71+
{
72+
var cx = x as IComparable;
73+
var cy = y as IComparable;
74+
75+
// ReSharper disable once PossibleUnintendedReferenceComparison
76+
return cx == cy ? 0 : cx == null ? -1 : cy == null ? +1 : cx.CompareTo(cy);
77+
}
3978
}
4079
}
4180
}

UnitTests/UI/Person.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,29 @@
1010
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
1111
// ******************************************************************
1212

13+
using System;
14+
1315
namespace UnitTests.UI
1416
{
1517
/// <summary>
1618
/// Sample class to test AdvancedCollectionViewSource functionality
1719
/// </summary>
18-
internal class Person
20+
internal class Person : IComparable
1921
{
2022
public string Name { get; set; }
2123

2224
public int Age { get; set; }
25+
26+
public int CompareTo(object obj)
27+
{
28+
var other = obj as Person;
29+
30+
if (other == null)
31+
{
32+
return -1;
33+
}
34+
35+
return Age.CompareTo(other.Age);
36+
}
2337
}
2438
}

UnitTests/UI/Test_AdvancedCollectionView.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
1111
// ******************************************************************
1212

13+
using System;
14+
using System.Collections;
1315
using System.Collections.Generic;
1416
using System.Collections.ObjectModel;
1517
using System.Linq;
1618
using Microsoft.Toolkit.Uwp.UI;
1719
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
1820
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer;
21+
1922
using Assert = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.Assert;
2023

2124
namespace UnitTests.UI
@@ -180,5 +183,164 @@ public void Test_AdvancedCollectionView_Combined()
180183
Assert.AreEqual(((Person)a.First()).Age, 42);
181184
Assert.AreEqual(a.Count, 2);
182185
}
186+
187+
[TestCategory("AdvancedCollectionView")]
188+
[UITestMethod]
189+
public void Test_AdvancedCollectionView_Sorting_OnSelf()
190+
{
191+
var l = new ObservableCollection<Person>
192+
{
193+
new Person()
194+
{
195+
Name = "lorem",
196+
Age = 4
197+
},
198+
new Person()
199+
{
200+
Name = "imsum",
201+
Age = 8
202+
},
203+
new Person()
204+
{
205+
Name = "dolor",
206+
Age = 15
207+
},
208+
new Person()
209+
{
210+
Name = "sit",
211+
Age = 16
212+
},
213+
new Person()
214+
{
215+
Name = "amet",
216+
Age = 23
217+
},
218+
new Person()
219+
{
220+
Name = "consectetur",
221+
Age = 42
222+
},
223+
};
224+
225+
var a = new AdvancedCollectionView(l)
226+
{
227+
SortDescriptions =
228+
{
229+
new SortDescription(SortDirection.Descending)
230+
}
231+
};
232+
233+
Assert.AreEqual(((Person)a.First()).Age, 42);
234+
}
235+
236+
[TestCategory("AdvancedCollectionView")]
237+
[UITestMethod]
238+
public void Test_AdvancedCollectionView_Sorting_OnSelf_CustomComparable()
239+
{
240+
var l = new ObservableCollection<Person>
241+
{
242+
new Person()
243+
{
244+
Name = "lorem",
245+
Age = 4
246+
},
247+
new Person()
248+
{
249+
Name = "imsum",
250+
Age = 8
251+
},
252+
new Person()
253+
{
254+
Name = "dolor",
255+
Age = 15
256+
},
257+
new Person()
258+
{
259+
Name = "sit",
260+
Age = 16
261+
},
262+
new Person()
263+
{
264+
Name = "amet",
265+
Age = 23
266+
},
267+
new Person()
268+
{
269+
Name = "consectetur",
270+
Age = 42
271+
},
272+
};
273+
274+
var a = new AdvancedCollectionView(l)
275+
{
276+
SortDescriptions =
277+
{
278+
new SortDescription(SortDirection.Ascending, new DelegateComparable((x, y) => -((Person)x).Age.CompareTo(((Person)y).Age)))
279+
}
280+
};
281+
282+
Assert.AreEqual(((Person)a.First()).Age, 42);
283+
}
284+
285+
[TestCategory("AdvancedCollectionView")]
286+
[UITestMethod]
287+
public void Test_AdvancedCollectionView_Sorting_CustomComparable()
288+
{
289+
var l = new ObservableCollection<Person>
290+
{
291+
new Person()
292+
{
293+
Name = "lorem",
294+
Age = 4
295+
},
296+
new Person()
297+
{
298+
Name = "imsum",
299+
Age = 8
300+
},
301+
new Person()
302+
{
303+
Name = "dolor",
304+
Age = 15
305+
},
306+
new Person()
307+
{
308+
Name = "sit",
309+
Age = 16
310+
},
311+
new Person()
312+
{
313+
Name = "amet",
314+
Age = 23
315+
},
316+
new Person()
317+
{
318+
Name = "consectetur",
319+
Age = 42
320+
},
321+
};
322+
323+
var a = new AdvancedCollectionView(l)
324+
{
325+
SortDescriptions =
326+
{
327+
new SortDescription(nameof(Person.Age), SortDirection.Ascending, new DelegateComparable((x, y) => -((int)x).CompareTo((int)y)))
328+
}
329+
};
330+
331+
Assert.AreEqual(((Person)a.First()).Age, 42);
332+
}
333+
334+
private class DelegateComparable : IComparer
335+
{
336+
private Func<object, object, int> _func;
337+
338+
public DelegateComparable(Func<object, object, int> func)
339+
{
340+
_func = func;
341+
}
342+
343+
public int Compare(object x, object y) => _func(x, y);
344+
}
183345
}
184346
}

0 commit comments

Comments
 (0)