[skip ci] [WIP] Change DerivedCollection to DynamicData
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinJoinTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinJoinTabViewModel.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
-using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
@@ -47,14 +46,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
Password = "";
- var onCoinsSetModified = Observable.FromEventPattern(Global.WalletService.Coins, nameof(Global.WalletService.Coins.HashSetChanged))
- .ObserveOn(RxApp.MainThreadScheduler);
-
- var globalCoins = Global.WalletService.Coins.CreateDerivedCollection(c => new CoinViewModel(c), null, (first, second) => second.Amount.CompareTo(first.Amount), signalReset: onCoinsSetModified, RxApp.MainThreadScheduler);
- globalCoins.ChangeTrackingEnabled = true;
-
- var coins = globalCoins.CreateDerivedCollection(c => c, c => c.Unspent);
-
var registrableRound = Global.ChaumianClient.State.GetRegistrableRoundOrDefault();
UpdateRequiredBtcLabel(registrableRound);
@@ -70,11 +61,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
if (!(registrableRound?.State?.Denomination is null) && registrableRound.State.Denomination != Money.Zero)
{
- CoinsList = new CoinListViewModel(coins, RequiredBTC, PreSelectMaxAnonSetExcludingCondition);
+ CoinsList = new CoinListViewModel(RequiredBTC, PreSelectMaxAnonSetExcludingCondition);
}
else
{
- CoinsList = new CoinListViewModel(coins);
+ CoinsList = new CoinListViewModel();
}
//CoinsList = new CoinListViewModel(coins);

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinListViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinListViewModel.cs
@@ -9,14 +9,34 @@ using WalletWasabi.Gui.Models;
using WalletWasabi.Gui.ViewModels;
using System.ComponentModel;
using System.Collections.Specialized;
+using WalletWasabi.Models;
+using System.Collections.ObjectModel;
+using System.Reactive.Linq;
+using Avalonia.Threading;
+using DynamicData;
+using DynamicData.Binding;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
public class CoinListViewModel : ViewModelBase
{
+
+ public ReadOnlyObservableCollection<CoinViewModel> Coins => _coinViewModels;
+ private readonly ReadOnlyObservableCollection<CoinViewModel> _coinViewModels;
+
+ SourceList<SmartCoin> _rootlist = new SourceList<SmartCoin>();
+ SortExpressionComparer<CoinViewModel> _myComparer;
+ SortExpressionComparer<CoinViewModel> MyComparer
+ {
+ get => _myComparer;
+ set
+ {
+ this.RaiseAndSetIfChanged(ref _myComparer, value);
+ }
+ }
+
#pragma warning disable CS0618 // Type or member is obsolete
- private IReactiveDerivedList<CoinViewModel> _coins;
- private IReactiveDerivedList<CoinViewModel> _rootcoinlist;
+
private CoinViewModel _selectedCoin;
private bool? _selectAllCheckBoxState;
private SortOrder _statusSortDirection;
@@ -26,6 +46,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private bool? _selectNonPrivateCheckBoxState;
private GridLength _coinJoinStatusWidth;
private SortOrder _historySortDirection;
+ private SortExpressionComparer<CoinViewModel> _myComparer1;
public ReactiveCommand EnqueueCoin { get; }
public ReactiveCommand DequeueCoin { get; }
@@ -71,20 +92,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
PrivacySortDirection = SortOrder.None;
HistorySortDirection = SortOrder.None;
}
-
- switch (value)
- {
- case SortOrder.Increasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => x.Status.CompareTo(y.Status));
- break;
-
- case SortOrder.Decreasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => y.Status.CompareTo(x.Status));
- break;
- }
}
}
+
public SortOrder AmountSortDirection
{
get => _amountSortDirection;
@@ -97,17 +108,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
StatusSortDirection = SortOrder.None;
HistorySortDirection = SortOrder.None;
}
-
- switch (value)
- {
- case SortOrder.Increasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => x.Amount.CompareTo(y.Amount));
- break;
-
- case SortOrder.Decreasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => y.Amount.CompareTo(x.Amount));
- break;
- }
+ RefreshOrdering();
}
}
@@ -123,17 +124,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
StatusSortDirection = SortOrder.None;
HistorySortDirection = SortOrder.None;
}
-
- switch (value)
- {
- case SortOrder.Increasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => x.AnonymitySet.CompareTo(y.AnonymitySet));
- break;
-
- case SortOrder.Decreasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => y.AnonymitySet.CompareTo(x.AnonymitySet));
- break;
- }
}
}
@@ -149,17 +139,17 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
StatusSortDirection = SortOrder.None;
PrivacySortDirection = SortOrder.None;
}
+ }
+ }
- switch (value)
+ void RefreshOrdering()
{
- case SortOrder.Increasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => string.Compare(x.History, y.History, StringComparison.Ordinal));
- break;
-
- case SortOrder.Decreasing:
- Coins = _rootcoinlist.CreateDerivedCollection(x => x, x => true, (x, y) => string.Compare(y.History, x.History, StringComparison.Ordinal));
- break;
- }
+ if (AmountSortDirection != SortOrder.None)
+ {
+ if (AmountSortDirection == SortOrder.Increasing)
+ MyComparer = SortExpressionComparer<CoinViewModel>.Ascending(cmv => cmv.Amount);
+ else
+ MyComparer = SortExpressionComparer<CoinViewModel>.Descending(cmv => cmv.Amount);
}
}
@@ -206,14 +196,28 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
- public CoinListViewModel(IReactiveDerivedList<CoinViewModel> coins, Money preSelectMinAmountIncludingCondition = null, int? preSelectMaxAnonSetExcludingCondition = null)
+ public CoinListViewModel(Money preSelectMinAmountIncludingCondition = null, int? preSelectMaxAnonSetExcludingCondition = null)
{
- _rootcoinlist = coins;
- Coins = coins;
+ MyComparer = SortExpressionComparer<CoinViewModel>.Ascending(cmv => cmv.Amount);
+
+ //IObservable<IComparer<CoinViewModel>> comparerObservable = MyComparer.Subscribe();
+
+ _rootlist.AddRange(Global.WalletService.Coins);
+
+ _rootlist.Connect()
+ .Transform(sc => new CoinViewModel(sc))
+ .OnItemAdded(cvm => cvm.PropertyChanged += Coin_PropertyChanged)
+ .OnItemRemoved(cvm => cvm.PropertyChanged -= Coin_PropertyChanged)
+ .Filter(cvm => cvm.Unspent)
+ .Sort(MyComparer)
+ .Bind(out _coinViewModels)
+ .Subscribe();
+
+ Global.WalletService.Coins.CollectionChanged += Coins_CollectionGlobalChanged;
if (preSelectMinAmountIncludingCondition != null && preSelectMaxAnonSetExcludingCondition != null)
{
- foreach (CoinViewModel coin in coins)
+ foreach (CoinViewModel coin in Coins)
{
if (coin.Amount >= preSelectMinAmountIncludingCondition && coin.AnonymitySet < preSelectMaxAnonSetExcludingCondition)
{
@@ -221,12 +225,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
}
- foreach (CoinViewModel coin in coins)
- {
- coin.PropertyChanged += Coin_PropertyChanged;
- }
- coins.CollectionChanging += Coins_CollectionChanging;
- coins.CollectionChanged += Coins_CollectionChanged;
EnqueueCoin = ReactiveCommand.Create(() =>
{
@@ -242,6 +240,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
SelectAllCheckBoxCommand = ReactiveCommand.Create(() =>
{
+ Global.WalletService.Coins.TryRemove(Coins[0].Model);
switch (SelectAllCheckBoxState)
{
case true:
@@ -298,9 +297,32 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
});
SetSelections();
SetCoinJoinStatusWidth();
- AmountSortDirection = SortOrder.Decreasing;
+ //AmountSortDirection = SortOrder.Decreasing;
+ }
+
+
+ void Coins_CollectionGlobalChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Dispatcher.UIThread.Post(() =>
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ foreach (var c in e.NewItems.Cast<SmartCoin>())
+ _rootlist.Add(c);
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ foreach (var c in e.OldItems.Cast<SmartCoin>())
+ _rootlist.Remove(c);
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ _rootlist.Clear();
+ break;
+ }
+ });
}
+
private void SetSelections()
{
SelectAllCheckBoxState = GetCheckBoxesSelectedState(x => true);
@@ -326,24 +348,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
- private void Coins_CollectionChanging(object sender, NotifyCollectionChangedEventArgs e)
- {
- foreach (CoinViewModel coin in Coins)
- {
- coin.PropertyChanged -= Coin_PropertyChanged;
- }
- }
-
- private void Coins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
- {
- foreach (CoinViewModel coin in Coins)
- {
- coin.PropertyChanged += Coin_PropertyChanged;
- }
- SetSelections();
- SetCoinJoinStatusWidth();
- }
-
private void Coin_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(CoinViewModel.IsSelected))
@@ -356,12 +360,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
- public IReactiveDerivedList<CoinViewModel> Coins
- {
- get { return _coins; }
- set { this.RaiseAndSetIfChanged(ref _coins, value); }
- }
-
#pragma warning restore CS0618 // Type or member is obsolete
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
@@ -45,15 +45,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
Label = "";
- var onCoinsSetModified = Observable.FromEventPattern(Global.WalletService.Coins, nameof(Global.WalletService.Coins.HashSetChanged))
- .ObserveOn(RxApp.MainThreadScheduler);
-
- var globalCoins = Global.WalletService.Coins.CreateDerivedCollection(c => new CoinViewModel(c), null, (first, second) => second.Amount.CompareTo(first.Amount), signalReset: onCoinsSetModified, RxApp.MainThreadScheduler);
- globalCoins.ChangeTrackingEnabled = true;
-
- var filteredCoins = globalCoins.CreateDerivedCollection(c => c, c => c.Unspent);
-
- CoinList = new CoinListViewModel(filteredCoins);
+ CoinList = new CoinListViewModel();
BuildTransactionButtonText = BuildTransactionButtonTextString;

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/WalletViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/WalletViewModel.cs
@@ -77,7 +77,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private void SetBalance(string walletName)
{
- Money balance = Global.WalletService.Coins.Where(c => c.Unspent).Sum(c => (long?)c.Amount) ?? 0;
+ Money balance = Enumerable.Where(Global.WalletService.Coins, c => c.Unspent).Sum(c => (long?)c.Amount) ?? 0;
Title = $"{walletName} ({balance.ToString(false, true)} BTC)";
}
}

MODIFY
WalletWasabi/Models/ObservableConcurrentHashSet.cs
WalletWasabi/Models/ObservableConcurrentHashSet.cs
@@ -8,12 +8,13 @@ using System.Text;
namespace WalletWasabi.Models
{
- public class ObservableConcurrentHashSet<T> : IReadOnlyCollection<T> // DO NOT IMPLEMENT INotifyCollectionChanged!!! That'll break and crash the software: https://github.com/AvaloniaUI/Avalonia/issues/1988#issuecomment-431691863
+ public class ObservableConcurrentHashSet<T> : IReadOnlyCollection<T>,INotifyCollectionChanged, IObservable<T>// DO NOT IMPLEMENT INotifyCollectionChanged!!! That'll break and crash the software: https://github.com/AvaloniaUI/Avalonia/issues/1988#issuecomment-431691863
{
private ConcurrentHashSet<T> Set { get; }
private object Lock { get; }
public event EventHandler HashSetChanged; // Keep it as is! Unless with the modification this bug won't come out: https://github.com/AvaloniaUI/Avalonia/issues/1988#issuecomment-431691863
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
public ObservableConcurrentHashSet()
{
@@ -36,6 +37,7 @@ namespace WalletWasabi.Models
if (Set.Add(item))
{
HashSetChanged?.Invoke(this, EventArgs.Empty);
+ CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,new[] {item}));
return true;
}
return false;
@@ -49,6 +51,7 @@ namespace WalletWasabi.Models
if (Set.TryRemove(item))
{
HashSetChanged?.Invoke(this, EventArgs.Empty);
+ CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { item }));
return true;
}
return false;
@@ -63,11 +66,17 @@ namespace WalletWasabi.Models
{
Set.Clear();
HashSetChanged?.Invoke(this, EventArgs.Empty);
+ CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
// Don't lock here, it results deadlock at wallet loading when filters arent synced.
public bool Contains(T item) => Set.Contains(item);
+
+ public IDisposable Subscribe(IObserver<T> observer)
+ {
+ throw new NotImplementedException();
+ }
}
}

[skip ci] [WIP] Change DerivedCollection to DynamicData