-
Notifications
You must be signed in to change notification settings - Fork 13
/
XamarinFormsGrouping.cs
96 lines (79 loc) · 3.54 KB
/
XamarinFormsGrouping.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using DynamicData.Binding;
using DynamicData.Snippets.Infrastructure;
namespace DynamicData.Snippets.Group
{
public sealed class XamarinFormsGrouping: AbstractNotifyPropertyChanged, IDisposable
{
private readonly IDisposable _cleanUp;
public ReadOnlyObservableCollection<AnimalGroup> FamilyGroups { get; }
public XamarinFormsGrouping(IObservableList<Animal> source, ISchedulerProvider schedulerProvider)
{
/* Xamarin forms is a bit dumb and cannot handle nested observable collections.
* To cirumvent this limitation, create a specialist observable collection with headers and use dynamic data to manage it */
//create an observable predicate
var observablePredicate = this.WhenValueChanged(@this => @this.Filter).ObserveOn(schedulerProvider.Background);
_cleanUp = source.Connect()
.Filter(observablePredicate) //Apply filter dynamically
.GroupOn(arg => arg.Family) //create a dynamic group
.Transform(grouping => new AnimalGroup(grouping, schedulerProvider)) //transform into a specialised observable collection
.Sort(SortExpressionComparer<AnimalGroup>.Ascending(a => a.Family))
.ObserveOn(schedulerProvider.MainThread)
.Bind(out var animals)
.DisposeMany() //use DisposeMany() because the grouping is disposable
.Subscribe();
FamilyGroups = animals;
}
private Func<Animal, bool> _filter = a => true;
public Func<Animal, bool> Filter
{
get => _filter;
set => SetAndRaise(ref _filter, value);
}
public void Dispose()
{
_cleanUp.Dispose();
}
}
[DebuggerDisplay("{Header}")]
public sealed class AnimalGroup : ObservableCollectionExtended<Animal>, IDisposable
{
private readonly IDisposable _cleanUp;
public AnimalFamily Family { get; }
public AnimalGroup(IGroup<Animal, AnimalFamily> grouping, ISchedulerProvider schedulerProvider)
{
this.Family = grouping.GroupKey;
//load and sort the grouped list
var dataLoader = grouping.List.Connect()
.Sort(SortExpressionComparer<Animal>.Ascending(a => a.Name).ThenByAscending(a => a.Type))
.ObserveOn(schedulerProvider.MainThread)
.Bind(this, 2000) //make the reset threshold large because xamarin is slow when reset is called (or at least I think it is @erlend, please enlighten me )
.Subscribe();
//set the header when the group coount changes
var headerSetter = grouping.List.CountChanged
.Select(count => $"{grouping.GroupKey} - {count} items(s)")
.ObserveOn(schedulerProvider.MainThread)
.Subscribe(text => Header = text);
_cleanUp = new CompositeDisposable(dataLoader, headerSetter);
}
string _header;
public string Header
{
get => _header;
set
{
_header = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Header)));
}
}
public void Dispose()
{
_cleanUp.Dispose();
}
}
}